Skip to content

Commit

Permalink
Async Before-/AfterTransactionCompletionProcess (#1452)
Browse files Browse the repository at this point in the history
* Replace TransactionCompletion delegates with interfaces

Co-authored-by: Alexander Zaytsev <[email protected]>
  • Loading branch information
gliljas and hazzik committed Sep 16, 2018
1 parent f7bc251 commit ea4c7ff
Show file tree
Hide file tree
Showing 22 changed files with 500 additions and 140 deletions.
9 changes: 9 additions & 0 deletions src/AsyncGenerator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
rule: EventListener
- conversion: ToAsync
rule: Cache
- conversion: ToAsync
rule: TransactionCompletion
typeConversion:
- conversion: Ignore
name: EnumerableImpl
Expand Down Expand Up @@ -141,6 +143,7 @@
requiresCancellationToken:
- rule: EventListener
- rule: Cache
- rule: TransactionCompletion
scanMethodBody: true
scanForMissingAsyncMembers:
- all: true
Expand Down Expand Up @@ -276,6 +279,12 @@ methodRules:
- containingType: NHibernate.Cache.IBatchableReadOnlyCache
- containingType: NHibernate.Cache.IBatchableCache
name: Cache
- filters:
- containingType: NHibernate.Action.IAfterTransactionCompletionProcess
- containingType: NHibernate.Action.IBeforeTransactionCompletionProcess
- containingType: NHibernate.Action.EntityAction
name: BeforeTransactionCompletionProcessImpl
name: TransactionCompletion
- filters:
- containingNamespace: NHibernate
- containingType: NHibernate.Tool.hbm2ddl.SchemaUpdate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@


using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NHibernate.Cache;
using NHibernate.Cfg;
using NSubstitute;
Expand Down Expand Up @@ -40,7 +40,7 @@ protected override void Configure(Configuration configuration)
public async Task SimpleNativeSQLInsert_DoesNotEvictEntireCacheWhenQuerySpacesAreAddedAsync()
{
List<string> clearCalls = new List<string>();
(Sfi.Settings.CacheProvider as SubstituteCacheProvider).OnClear(x =>
((SubstituteCacheProvider) Sfi.Settings.CacheProvider).OnClear(x =>
{
clearCalls.Add(x);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void AfterTransactionCompletionProcess_EvictsFromCache(string querySpaces

var target = new BulkOperationCleanupAction(_session, new HashSet<string>(querySpaces.Split(new []{','},StringSplitOptions.RemoveEmptyEntries)));

target.AfterTransactionCompletionProcess(true);
target.ExecuteAfterTransactionCompletion(true);

Assert.AreEqual(expectedPropertySpaceLength, target.PropertySpaces.Length);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NHibernate.Cache;
using NHibernate.Cfg;
using NSubstitute;
Expand All @@ -29,7 +29,7 @@ protected override void Configure(Configuration configuration)
public void SimpleNativeSQLInsert_DoesNotEvictEntireCacheWhenQuerySpacesAreAdded()
{
List<string> clearCalls = new List<string>();
(Sfi.Settings.CacheProvider as SubstituteCacheProvider).OnClear(x =>
((SubstituteCacheProvider) Sfi.Settings.CacheProvider).OnClear(x =>
{
clearCalls.Add(x);
});
Expand Down Expand Up @@ -71,6 +71,7 @@ public ICache BuildCache(string regionName, IDictionary<string, string> properti
var cache = Substitute.For<ICache>();
cache.RegionName.Returns(regionName);
cache.When(c => c.Clear()).Do(c => _onClear?.Invoke(regionName));
cache.When(c => c.ClearAsync(Arg.Any<CancellationToken>())).Do(c => _onClear?.Invoke(regionName));
return cache;
})).Value;
}
Expand Down
36 changes: 19 additions & 17 deletions src/NHibernate/Action/BulkOperationCleanupAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace NHibernate.Action
/// Implementation of BulkOperationCleanupAction.
/// </summary>
[Serializable]
public partial class BulkOperationCleanupAction : IExecutable
public partial class BulkOperationCleanupAction : IAsyncExecutable, IAfterTransactionCompletionProcess
{
private readonly ISessionImplementor session;
private readonly HashSet<string> affectedEntityNames = new HashSet<string>();
Expand Down Expand Up @@ -109,24 +109,26 @@ public void Execute()
// nothing to do
}

public BeforeTransactionCompletionProcessDelegate BeforeTransactionCompletionProcess
{
get
{
return null;
}
}
//Since v5.2
[Obsolete("This property is not used and will be removed in a future version.")]
public BeforeTransactionCompletionProcessDelegate BeforeTransactionCompletionProcess =>
null;

//Since v5.2
[Obsolete("This property is not used and will be removed in a future version.")]
public AfterTransactionCompletionProcessDelegate AfterTransactionCompletionProcess =>
ExecuteAfterTransactionCompletion;

IBeforeTransactionCompletionProcess IAsyncExecutable.BeforeTransactionCompletionProcess =>
null;

public AfterTransactionCompletionProcessDelegate AfterTransactionCompletionProcess
IAfterTransactionCompletionProcess IAsyncExecutable.AfterTransactionCompletionProcess =>
this;

public void ExecuteAfterTransactionCompletion(bool success)
{
get
{
return new AfterTransactionCompletionProcessDelegate((success) =>
{
this.EvictEntityRegions();
this.EvictCollectionRegions();
});
}
EvictEntityRegions();
EvictCollectionRegions();
}

private void EvictCollectionRegions()
Expand Down
44 changes: 20 additions & 24 deletions src/NHibernate/Action/CollectionAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace NHibernate.Action
/// Any action relating to insert/update/delete of a collection
/// </summary>
[Serializable]
public abstract partial class CollectionAction : IExecutable, IComparable<CollectionAction>, IDeserializationCallback
public abstract partial class CollectionAction : IAsyncExecutable, IComparable<CollectionAction>, IDeserializationCallback, IAfterTransactionCompletionProcess
{
private readonly object key;
private object finalKey;
Expand Down Expand Up @@ -104,32 +104,28 @@ public virtual void BeforeExecutions()
/// <summary>Execute this action</summary>
public abstract void Execute();

public virtual BeforeTransactionCompletionProcessDelegate BeforeTransactionCompletionProcess
{
get
{
return null;
}
}
IBeforeTransactionCompletionProcess IAsyncExecutable.BeforeTransactionCompletionProcess =>
null;

public virtual AfterTransactionCompletionProcessDelegate AfterTransactionCompletionProcess
{
IAfterTransactionCompletionProcess IAsyncExecutable.AfterTransactionCompletionProcess =>
persister.HasCache ? this : null;

get
{
// Only make sense to add the delegate if there is a cache.
if (persister.HasCache)
{
return new AfterTransactionCompletionProcessDelegate((success) =>
{
CacheKey ck = new CacheKey(key, persister.KeyType, persister.Role, Session.Factory);
persister.Cache.Release(ck, softLock);
});
}
return null;
}
//Since v5.2
[Obsolete("This property is not used and will be removed in a future version.")]
public virtual BeforeTransactionCompletionProcessDelegate BeforeTransactionCompletionProcess =>
null;

//Since v5.2
[Obsolete("This property is not used and will be removed in a future version.")]
public virtual AfterTransactionCompletionProcessDelegate AfterTransactionCompletionProcess =>
persister.HasCache ? ExecuteAfterTransactionCompletion : default(AfterTransactionCompletionProcessDelegate);

public virtual void ExecuteAfterTransactionCompletion(bool success)
{
var ck = new CacheKey(key, persister.KeyType, persister.Role, Session.Factory);
persister.Cache.Release(ck, softLock);
}

#endregion

public ISoftLock Lock
Expand Down
54 changes: 18 additions & 36 deletions src/NHibernate/Action/CollectionUpdateAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,48 +113,30 @@ private void PostUpdate()
}
}

public override BeforeTransactionCompletionProcessDelegate BeforeTransactionCompletionProcess
public override void ExecuteAfterTransactionCompletion(bool success)
{
get
{
return null;
}
}
// NH Different behavior: to support unlocking collections from the cache.(r3260)

public override AfterTransactionCompletionProcessDelegate AfterTransactionCompletionProcess
{
get
CacheKey ck = Session.GenerateCacheKey(Key, Persister.KeyType, Persister.Role);

if (success)
{
// Only make sense to add the delegate if there is a cache.
if (Persister.HasCache)
// we can't disassemble a collection if it was uninitialized
// or detached from the session
if (Collection.WasInitialized && Session.PersistenceContext.ContainsCollection(Collection))
{
// NH Different behavior: to support unlocking collections from the cache.(r3260)
return new AfterTransactionCompletionProcessDelegate((success) =>
CollectionCacheEntry entry = CollectionCacheEntry.Create(Collection, Persister);
bool put = Persister.Cache.AfterUpdate(ck, entry, null, Lock);

if (put && Session.Factory.Statistics.IsStatisticsEnabled)
{
CacheKey ck = Session.GenerateCacheKey(Key, Persister.KeyType, Persister.Role);

if (success)
{
// we can't disassemble a collection if it was uninitialized
// or detached from the session
if (Collection.WasInitialized && Session.PersistenceContext.ContainsCollection(Collection))
{
CollectionCacheEntry entry = CollectionCacheEntry.Create(Collection, Persister);
bool put = Persister.Cache.AfterUpdate(ck, entry, null, Lock);

if (put && Session.Factory.Statistics.IsStatisticsEnabled)
{
Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName);
}
}
}
else
{
Persister.Cache.Release(ck, Lock);
}
});
Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName);
}
}
return null;
}
else
{
Persister.Cache.Release(ck, Lock);
}
}
}
Expand Down
57 changes: 36 additions & 21 deletions src/NHibernate/Action/EntityAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ namespace NHibernate.Action
/// instance.
/// </summary>
[Serializable]
public abstract partial class EntityAction : IExecutable, IComparable<EntityAction>, IDeserializationCallback
public abstract partial class EntityAction :
IAsyncExecutable,
IBeforeTransactionCompletionProcess,
IAfterTransactionCompletionProcess,
IComparable<EntityAction>,
IDeserializationCallback
{
private readonly string entityName;
private readonly object id;
Expand Down Expand Up @@ -102,26 +107,26 @@ public void BeforeExecutions()

public abstract void Execute();

public virtual BeforeTransactionCompletionProcessDelegate BeforeTransactionCompletionProcess
{
get
{
return NeedsBeforeTransactionCompletion()
? new BeforeTransactionCompletionProcessDelegate(BeforeTransactionCompletionProcessImpl)
: null;
}
}

public virtual AfterTransactionCompletionProcessDelegate AfterTransactionCompletionProcess
{
get
{
return NeedsAfterTransactionCompletion()
? new AfterTransactionCompletionProcessDelegate(AfterTransactionCompletionProcessImpl)
: null;
}
}
//Since v5.2
[Obsolete("This property is not used and will be removed in a future version.")]
public virtual BeforeTransactionCompletionProcessDelegate BeforeTransactionCompletionProcess =>
NeedsBeforeTransactionCompletion()
? BeforeTransactionCompletionProcessImpl
: default(BeforeTransactionCompletionProcessDelegate);

//Since v5.2
[Obsolete("This property is not used and will be removed in a future version.")]
public virtual AfterTransactionCompletionProcessDelegate AfterTransactionCompletionProcess =>
NeedsAfterTransactionCompletion()
? AfterTransactionCompletionProcessImpl
: default(AfterTransactionCompletionProcessDelegate);

IBeforeTransactionCompletionProcess IAsyncExecutable.BeforeTransactionCompletionProcess =>
NeedsBeforeTransactionCompletion() ? this : null;

IAfterTransactionCompletionProcess IAsyncExecutable.AfterTransactionCompletionProcess =>
NeedsAfterTransactionCompletion() ? this : null;

protected virtual bool NeedsAfterTransactionCompletion()
{
return persister.HasCache || HasPostCommitEventListeners;
Expand Down Expand Up @@ -180,5 +185,15 @@ public override string ToString()
{
return StringHelper.Unqualify(GetType().FullName) + MessageHelper.InfoString(entityName, id);
}

public void ExecuteBeforeTransactionCompletion()
{
BeforeTransactionCompletionProcessImpl();
}

public void ExecuteAfterTransactionCompletion(bool success)
{
AfterTransactionCompletionProcessImpl(success);
}
}
}
14 changes: 14 additions & 0 deletions src/NHibernate/Action/IAfterTransactionCompletionProcess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace NHibernate.Action
{
/// <summary>
/// Contract representing some process that needs to occur during after transaction completion.
/// </summary>
public partial interface IAfterTransactionCompletionProcess
{
/// <summary>
/// Perform whatever processing is encapsulated here after completion of the transaction.
/// </summary>
/// <param name="success">Did the transaction complete successfully? True means it did.</param>
void ExecuteAfterTransactionCompletion(bool success);
}
}
20 changes: 20 additions & 0 deletions src/NHibernate/Action/IAsyncExecutable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace NHibernate.Action
{
/// <summary>
/// An extension to <see cref="IExecutable"/> which allows async cleanup operations to be
/// scheduled on transaction completion.
/// </summary>
//6.0 TODO: Merge into IExecutable
public interface IAsyncExecutable : IExecutable
{
/// <summary>
/// Get the before-transaction-completion process, if any, for this action.
/// </summary>
new IBeforeTransactionCompletionProcess BeforeTransactionCompletionProcess { get; }

/// <summary>
/// Get the after-transaction-completion process, if any, for this action.
/// </summary>
new IAfterTransactionCompletionProcess AfterTransactionCompletionProcess { get; }
}
}
13 changes: 13 additions & 0 deletions src/NHibernate/Action/IBeforeTransactionCompletionProcess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace NHibernate.Action
{
/// <summary>
/// Contract representing some process that needs to occur during before transaction completion.
/// </summary>
public partial interface IBeforeTransactionCompletionProcess
{
/// <summary>
/// Perform whatever processing is encapsulated here before completion of the transaction.
/// </summary>
void ExecuteBeforeTransactionCompletion();
}
}
Loading

0 comments on commit ea4c7ff

Please sign in to comment.