From 778d71d4665ed8efd89bd69d65b1e214d24b267f Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Mon, 15 Apr 2019 23:56:16 +1200 Subject: [PATCH 1/4] Do not try to invalidate query spaces for non cached entities Fixes #2129 --- src/NHibernate/Action/BulkOperationCleanupAction.cs | 10 +++++++++- src/NHibernate/Action/CollectionAction.cs | 9 ++++++++- src/NHibernate/Action/EntityAction.cs | 5 ++++- src/NHibernate/Action/ICacheableExecutable.cs | 8 ++++++++ .../Async/Action/BulkOperationCleanupAction.cs | 5 ++++- src/NHibernate/Async/Action/CollectionAction.cs | 7 ++++++- src/NHibernate/Async/Action/EntityAction.cs | 3 ++- src/NHibernate/Engine/ActionQueue.cs | 3 ++- 8 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 src/NHibernate/Action/ICacheableExecutable.cs diff --git a/src/NHibernate/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Action/BulkOperationCleanupAction.cs index edd5b8f20bd..ed59dd2efed 100644 --- a/src/NHibernate/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Action/BulkOperationCleanupAction.cs @@ -14,12 +14,16 @@ namespace NHibernate.Action /// Implementation of BulkOperationCleanupAction. /// [Serializable] - public partial class BulkOperationCleanupAction : IAsyncExecutable, IAfterTransactionCompletionProcess + public partial class BulkOperationCleanupAction : + IAsyncExecutable, + IAfterTransactionCompletionProcess, + ICacheableExecutable { private readonly ISessionImplementor session; private readonly HashSet affectedEntityNames = new HashSet(); private readonly HashSet affectedCollectionRoles = new HashSet(); private readonly List spaces; + private readonly bool _hasCache; public BulkOperationCleanupAction(ISessionImplementor session, IQueryable[] affectedQueryables) { @@ -29,6 +33,7 @@ public BulkOperationCleanupAction(ISessionImplementor session, IQueryable[] affe { if (affectedQueryables[i].HasCache) { + _hasCache = true; affectedEntityNames.Add(affectedQueryables[i].EntityName); } ISet roles = session.Factory.GetCollectionRolesByEntityParticipant(affectedQueryables[i].EntityName); @@ -65,6 +70,7 @@ public BulkOperationCleanupAction(ISessionImplementor session, ISet quer { if (persister.HasCache) { + _hasCache = true; affectedEntityNames.Add(persister.EntityName); } ISet roles = session.Factory.GetCollectionRolesByEntityParticipant(persister.EntityName); @@ -91,6 +97,8 @@ private bool AffectedEntity(ISet querySpaces, string[] entitySpaces) return entitySpaces.Any(querySpaces.Contains); } + public bool HasCache => _hasCache; + #region IExecutable Members public string[] PropertySpaces diff --git a/src/NHibernate/Action/CollectionAction.cs b/src/NHibernate/Action/CollectionAction.cs index ca5be713bf6..ccd94d86bee 100644 --- a/src/NHibernate/Action/CollectionAction.cs +++ b/src/NHibernate/Action/CollectionAction.cs @@ -14,7 +14,12 @@ namespace NHibernate.Action /// Any action relating to insert/update/delete of a collection /// [Serializable] - public abstract partial class CollectionAction : IAsyncExecutable, IComparable, IDeserializationCallback, IAfterTransactionCompletionProcess + public abstract partial class CollectionAction: + IAsyncExecutable, + IComparable, + IDeserializationCallback, + IAfterTransactionCompletionProcess, + ICacheableExecutable { private readonly object key; [NonSerialized] private ICollectionPersister persister; @@ -77,6 +82,8 @@ protected internal ISessionImplementor Session get { return session; } } + public bool HasCache => persister.HasCache; + #region IExecutable Members /// diff --git a/src/NHibernate/Action/EntityAction.cs b/src/NHibernate/Action/EntityAction.cs index 70b2b1e60c2..0407684b385 100644 --- a/src/NHibernate/Action/EntityAction.cs +++ b/src/NHibernate/Action/EntityAction.cs @@ -18,7 +18,8 @@ public abstract partial class EntityAction : IBeforeTransactionCompletionProcess, IAfterTransactionCompletionProcess, IComparable, - IDeserializationCallback + IDeserializationCallback, + ICacheableExecutable { private readonly string entityName; private readonly object id; @@ -195,5 +196,7 @@ public void ExecuteAfterTransactionCompletion(bool success) { AfterTransactionCompletionProcessImpl(success); } + + public bool HasCache => persister.HasCache; } } diff --git a/src/NHibernate/Action/ICacheableExecutable.cs b/src/NHibernate/Action/ICacheableExecutable.cs new file mode 100644 index 00000000000..514cf4177b2 --- /dev/null +++ b/src/NHibernate/Action/ICacheableExecutable.cs @@ -0,0 +1,8 @@ +namespace NHibernate.Action +{ + //6.0 TODO: Merge to IExecutable + public interface ICacheableExecutable : IExecutable + { + bool HasCache { get; } + } +} \ No newline at end of file diff --git a/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs index 18d4553f24f..9db900901fe 100644 --- a/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs @@ -20,7 +20,10 @@ namespace NHibernate.Action { - public partial class BulkOperationCleanupAction : IAsyncExecutable, IAfterTransactionCompletionProcess + public partial class BulkOperationCleanupAction : + IAsyncExecutable, + IAfterTransactionCompletionProcess, + ICacheableExecutable { #region IExecutable Members diff --git a/src/NHibernate/Async/Action/CollectionAction.cs b/src/NHibernate/Async/Action/CollectionAction.cs index 16a2a27460e..bf64f7f9453 100644 --- a/src/NHibernate/Async/Action/CollectionAction.cs +++ b/src/NHibernate/Async/Action/CollectionAction.cs @@ -22,7 +22,12 @@ namespace NHibernate.Action { using System.Threading.Tasks; using System.Threading; - public abstract partial class CollectionAction : IAsyncExecutable, IComparable, IDeserializationCallback, IAfterTransactionCompletionProcess + public abstract partial class CollectionAction: + IAsyncExecutable, + IComparable, + IDeserializationCallback, + IAfterTransactionCompletionProcess, + ICacheableExecutable { protected async Task GetKeyAsync(CancellationToken cancellationToken) diff --git a/src/NHibernate/Async/Action/EntityAction.cs b/src/NHibernate/Async/Action/EntityAction.cs index a61ececcf29..778cc54d859 100644 --- a/src/NHibernate/Async/Action/EntityAction.cs +++ b/src/NHibernate/Async/Action/EntityAction.cs @@ -25,7 +25,8 @@ public abstract partial class EntityAction : IBeforeTransactionCompletionProcess, IAfterTransactionCompletionProcess, IComparable, - IDeserializationCallback + IDeserializationCallback, + ICacheableExecutable { #region IExecutable Members diff --git a/src/NHibernate/Engine/ActionQueue.cs b/src/NHibernate/Engine/ActionQueue.cs index 36a76591edd..8da60ffb554 100644 --- a/src/NHibernate/Engine/ActionQueue.cs +++ b/src/NHibernate/Engine/ActionQueue.cs @@ -216,7 +216,8 @@ private void RegisterCleanupActions(IExecutable executable) RegisterProcess(executable.AfterTransactionCompletionProcess); #pragma warning restore 618,619 } - if (executable.PropertySpaces != null) + + if (executable.PropertySpaces != null && (!(executable is ICacheableExecutable ce) || ce.HasCache)) { executedSpaces.UnionWith(executable.PropertySpaces); } From 1ffbd30c3091ce0853727b32fd70b9dfa0f44c06 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Tue, 16 Apr 2019 00:04:27 +1200 Subject: [PATCH 2/4] Code cleanup --- .../Action/BulkOperationCleanupAction.cs | 59 +++++++++---------- .../Action/BulkOperationCleanupAction.cs | 4 +- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/NHibernate/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Action/BulkOperationCleanupAction.cs index ed59dd2efed..e19f18743fe 100644 --- a/src/NHibernate/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Action/BulkOperationCleanupAction.cs @@ -19,34 +19,34 @@ public partial class BulkOperationCleanupAction : IAfterTransactionCompletionProcess, ICacheableExecutable { - private readonly ISessionImplementor session; + private readonly ISessionFactoryImplementor _factory; private readonly HashSet affectedEntityNames = new HashSet(); private readonly HashSet affectedCollectionRoles = new HashSet(); - private readonly List spaces; + private readonly string[] spaces; private readonly bool _hasCache; public BulkOperationCleanupAction(ISessionImplementor session, IQueryable[] affectedQueryables) { - this.session = session; - List tmpSpaces = new List(); - for (int i = 0; i < affectedQueryables.Length; i++) + _factory = session.Factory; + var tmpSpaces = new HashSet(); + foreach (var queryables in affectedQueryables) { - if (affectedQueryables[i].HasCache) + if (queryables.HasCache) { _hasCache = true; - affectedEntityNames.Add(affectedQueryables[i].EntityName); + affectedEntityNames.Add(queryables.EntityName); } - ISet roles = session.Factory.GetCollectionRolesByEntityParticipant(affectedQueryables[i].EntityName); + + var roles = _factory.GetCollectionRolesByEntityParticipant(queryables.EntityName); if (roles != null) { affectedCollectionRoles.UnionWith(roles); } - for (int y = 0; y < affectedQueryables[i].QuerySpaces.Length; y++) - { - tmpSpaces.Add(affectedQueryables[i].QuerySpaces[y]); - } + + tmpSpaces.UnionWith(queryables.QuerySpaces); } - spaces = new List(tmpSpaces); + + spaces = tmpSpaces.ToArray(); } /// @@ -55,16 +55,15 @@ public BulkOperationCleanupAction(ISessionImplementor session, IQueryable[] affe public BulkOperationCleanupAction(ISessionImplementor session, ISet querySpaces) { //from H3.2 TODO: cache the autodetected information and pass it in instead. - this.session = session; + _factory = session.Factory; - ISet tmpSpaces = new HashSet(querySpaces); - ISessionFactoryImplementor factory = session.Factory; - IDictionary acmd = factory.GetAllClassMetadata(); + var tmpSpaces = new HashSet(querySpaces); + var acmd = _factory.GetAllClassMetadata(); foreach (KeyValuePair entry in acmd) { - string entityName = entry.Key; - IEntityPersister persister = factory.GetEntityPersister(entityName); - string[] entitySpaces = persister.QuerySpaces; + var entityName = entry.Key; + var persister = _factory.GetEntityPersister(entityName); + var entitySpaces = persister.QuerySpaces; if (AffectedEntity(querySpaces, entitySpaces)) { @@ -73,18 +72,17 @@ public BulkOperationCleanupAction(ISessionImplementor session, ISet quer _hasCache = true; affectedEntityNames.Add(persister.EntityName); } - ISet roles = session.Factory.GetCollectionRolesByEntityParticipant(persister.EntityName); + + var roles = session.Factory.GetCollectionRolesByEntityParticipant(persister.EntityName); if (roles != null) { affectedCollectionRoles.UnionWith(roles); } - for (int y = 0; y < entitySpaces.Length; y++) - { - tmpSpaces.Add(entitySpaces[y]); - } + + tmpSpaces.UnionWith(entitySpaces); } } - spaces = new List(tmpSpaces); + spaces = tmpSpaces.ToArray(); } private bool AffectedEntity(ISet querySpaces, string[] entitySpaces) @@ -101,10 +99,7 @@ private bool AffectedEntity(ISet querySpaces, string[] entitySpaces) #region IExecutable Members - public string[] PropertySpaces - { - get { return spaces.ToArray(); } - } + public string[] PropertySpaces => spaces; public void BeforeExecutions() { @@ -142,7 +137,7 @@ private void EvictCollectionRegions() { if (affectedCollectionRoles != null && affectedCollectionRoles.Any()) { - session.Factory.EvictCollection(affectedCollectionRoles); + _factory.EvictCollection(affectedCollectionRoles); } } @@ -150,7 +145,7 @@ private void EvictEntityRegions() { if (affectedEntityNames != null && affectedEntityNames.Any()) { - session.Factory.EvictEntity(affectedEntityNames); + _factory.EvictEntity(affectedEntityNames); } } diff --git a/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs index 9db900901fe..ec5eb52e5f5 100644 --- a/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs @@ -79,7 +79,7 @@ private Task EvictCollectionRegionsAsync(CancellationToken cancellationToken) { if (affectedCollectionRoles != null && affectedCollectionRoles.Any()) { - return session.Factory.EvictCollectionAsync(affectedCollectionRoles, cancellationToken); + return _factory.EvictCollectionAsync(affectedCollectionRoles, cancellationToken); } return Task.CompletedTask; } @@ -99,7 +99,7 @@ private Task EvictEntityRegionsAsync(CancellationToken cancellationToken) { if (affectedEntityNames != null && affectedEntityNames.Any()) { - return session.Factory.EvictEntityAsync(affectedEntityNames, cancellationToken); + return _factory.EvictEntityAsync(affectedEntityNames, cancellationToken); } return Task.CompletedTask; } From e12a585af74498196eb953651830154874f485c6 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Mon, 29 Apr 2019 23:51:00 +1200 Subject: [PATCH 3/4] Initialize collections on demand --- .../Action/BulkOperationCleanupAction.cs | 28 ++++++++++++++++--- .../Action/BulkOperationCleanupAction.cs | 26 ++++------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/NHibernate/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Action/BulkOperationCleanupAction.cs index e19f18743fe..f3f2d69f023 100644 --- a/src/NHibernate/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Action/BulkOperationCleanupAction.cs @@ -20,8 +20,8 @@ public partial class BulkOperationCleanupAction : ICacheableExecutable { private readonly ISessionFactoryImplementor _factory; - private readonly HashSet affectedEntityNames = new HashSet(); - private readonly HashSet affectedCollectionRoles = new HashSet(); + private readonly HashSet affectedEntityNames; + private readonly HashSet affectedCollectionRoles; private readonly string[] spaces; private readonly bool _hasCache; @@ -34,12 +34,22 @@ public BulkOperationCleanupAction(ISessionImplementor session, IQueryable[] affe if (queryables.HasCache) { _hasCache = true; + if (affectedEntityNames == null) + { + affectedEntityNames = new HashSet(); + } + affectedEntityNames.Add(queryables.EntityName); } var roles = _factory.GetCollectionRolesByEntityParticipant(queryables.EntityName); if (roles != null) { + if (affectedCollectionRoles == null) + { + affectedCollectionRoles = new HashSet(); + } + affectedCollectionRoles.UnionWith(roles); } @@ -70,12 +80,22 @@ public BulkOperationCleanupAction(ISessionImplementor session, ISet quer if (persister.HasCache) { _hasCache = true; + if (affectedEntityNames == null) + { + affectedEntityNames = new HashSet(); + } + affectedEntityNames.Add(persister.EntityName); } var roles = session.Factory.GetCollectionRolesByEntityParticipant(persister.EntityName); if (roles != null) { + if (affectedCollectionRoles == null) + { + affectedCollectionRoles = new HashSet(); + } + affectedCollectionRoles.UnionWith(roles); } @@ -135,7 +155,7 @@ public void ExecuteAfterTransactionCompletion(bool success) private void EvictCollectionRegions() { - if (affectedCollectionRoles != null && affectedCollectionRoles.Any()) + if (affectedCollectionRoles != null) { _factory.EvictCollection(affectedCollectionRoles); } @@ -143,7 +163,7 @@ private void EvictCollectionRegions() private void EvictEntityRegions() { - if (affectedEntityNames != null && affectedEntityNames.Any()) + if (affectedEntityNames != null) { _factory.EvictEntity(affectedEntityNames); } diff --git a/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs index ec5eb52e5f5..1ee17a5ef9d 100644 --- a/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Async/Action/BulkOperationCleanupAction.cs @@ -75,18 +75,11 @@ private Task EvictCollectionRegionsAsync(CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - try - { - if (affectedCollectionRoles != null && affectedCollectionRoles.Any()) - { - return _factory.EvictCollectionAsync(affectedCollectionRoles, cancellationToken); - } - return Task.CompletedTask; - } - catch (Exception ex) + if (affectedCollectionRoles != null) { - return Task.FromException(ex); + return _factory.EvictCollectionAsync(affectedCollectionRoles, cancellationToken); } + return Task.CompletedTask; } private Task EvictEntityRegionsAsync(CancellationToken cancellationToken) @@ -95,18 +88,11 @@ private Task EvictEntityRegionsAsync(CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - try - { - if (affectedEntityNames != null && affectedEntityNames.Any()) - { - return _factory.EvictEntityAsync(affectedEntityNames, cancellationToken); - } - return Task.CompletedTask; - } - catch (Exception ex) + if (affectedEntityNames != null) { - return Task.FromException(ex); + return _factory.EvictEntityAsync(affectedEntityNames, cancellationToken); } + return Task.CompletedTask; } #endregion From b9efaac5e681d338207bfe125ab9a7e46dffee50 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Sun, 17 May 2020 14:26:27 +1200 Subject: [PATCH 4/4] Do not create executedSpaces if query cache is disabled --- src/NHibernate/Async/Engine/ActionQueue.cs | 7 +++---- src/NHibernate/Engine/ActionQueue.cs | 13 +++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/NHibernate/Async/Engine/ActionQueue.cs b/src/NHibernate/Async/Engine/ActionQueue.cs index 3a0ab25b1f5..f0207c5bd28 100644 --- a/src/NHibernate/Async/Engine/ActionQueue.cs +++ b/src/NHibernate/Async/Engine/ActionQueue.cs @@ -45,7 +45,7 @@ private Task PreInvalidateCachesAsync(CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - if (session.Factory.Settings.IsQueryCacheEnabled && executedSpaces.Count > 0) + if (executedSpaces?.Count > 0) { return session.Factory.UpdateTimestampsCache.PreInvalidateAsync(executedSpaces, cancellationToken); } @@ -165,12 +165,11 @@ public async Task AfterTransactionCompletionAsync(bool success, CancellationToke private async Task InvalidateCachesAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (session.Factory.Settings.IsQueryCacheEnabled && executedSpaces.Count > 0) + if (executedSpaces?.Count > 0) { await (session.Factory.UpdateTimestampsCache.InvalidateAsync(executedSpaces, cancellationToken)).ConfigureAwait(false); + executedSpaces.Clear(); } - - executedSpaces.Clear(); } private partial class BeforeTransactionCompletionProcessQueue { diff --git a/src/NHibernate/Engine/ActionQueue.cs b/src/NHibernate/Engine/ActionQueue.cs index 8da60ffb554..0f28cbf5284 100644 --- a/src/NHibernate/Engine/ActionQueue.cs +++ b/src/NHibernate/Engine/ActionQueue.cs @@ -59,7 +59,7 @@ public ActionQueue(ISessionImplementor session) afterTransactionProcesses = new AfterTransactionCompletionProcessQueue(); beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue(); - executedSpaces = new HashSet(); + executedSpaces = session.Factory.Settings.IsQueryCacheEnabled ? new HashSet() : null; } public virtual void Clear() @@ -172,7 +172,7 @@ private void ExecuteActions(List list) where T: IExecutable private void PreInvalidateCaches() { - if (session.Factory.Settings.IsQueryCacheEnabled && executedSpaces.Count > 0) + if (executedSpaces?.Count > 0) { session.Factory.UpdateTimestampsCache.PreInvalidate(executedSpaces); } @@ -217,7 +217,9 @@ private void RegisterCleanupActions(IExecutable executable) #pragma warning restore 618,619 } - if (executable.PropertySpaces != null && (!(executable is ICacheableExecutable ce) || ce.HasCache)) + if (executedSpaces != null && + executable.PropertySpaces != null && + (!(executable is ICacheableExecutable ce) || ce.HasCache)) { executedSpaces.UnionWith(executable.PropertySpaces); } @@ -295,12 +297,11 @@ public void AfterTransactionCompletion(bool success) private void InvalidateCaches() { - if (session.Factory.Settings.IsQueryCacheEnabled && executedSpaces.Count > 0) + if (executedSpaces?.Count > 0) { session.Factory.UpdateTimestampsCache.Invalidate(executedSpaces); + executedSpaces.Clear(); } - - executedSpaces.Clear(); } ///