Skip to content

Optimize GetOrphans and remove wrong checks from IsNotTransientSlow #2056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Oct 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 6 additions & 92 deletions src/NHibernate/Async/Collection/AbstractPersistentCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NHibernate.Collection.Generic;
using NHibernate.Engine;
using NHibernate.Impl;
Expand All @@ -22,8 +25,6 @@

namespace NHibernate.Collection
{
using System.Threading.Tasks;
using System.Threading;
public abstract partial class AbstractPersistentCollection : IPersistentCollection, ILazyInitializedCollection
{

Expand Down Expand Up @@ -91,42 +92,7 @@ public virtual Task ForceInitializationAsync(CancellationToken cancellationToken
}
return Task.CompletedTask;
}

public Task<ICollection> GetQueuedOrphansAsync(string entityName, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<ICollection>(cancellationToken);
}
try
{
if (HasQueuedOperations)
{
List<object> additions = new List<object>(operationQueue.Count);
List<object> removals = new List<object>(operationQueue.Count);
for (int i = 0; i < operationQueue.Count; i++)
{
IDelayedOperation op = operationQueue[i];
if (op.AddedInstance != null)
{
additions.Add(op.AddedInstance);
}
if (op.Orphan != null)
{
removals.Add(op.Orphan);
}
}
return GetOrphansAsync(removals, additions, entityName, session, cancellationToken);
}

return Task.FromResult<ICollection>(CollectionHelper.EmptyCollection);
}
catch (Exception ex)
{
return Task.FromException<ICollection>(ex);
}
}


/// <summary>
/// Called before inserting rows, to ensure that any surrogate keys are fully generated
/// </summary>
Expand All @@ -149,60 +115,8 @@ public virtual Task PreInsertAsync(ICollectionPersister persister, CancellationT
}
}

/// <summary>
/// Get all "orphaned" elements
/// </summary>
public abstract Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken);

/// <summary>
/// Given a collection of entity instances that used to
/// belong to the collection, and a collection of instances
/// that currently belong, return a collection of orphans
/// </summary>
protected virtual async Task<ICollection> GetOrphansAsync(ICollection oldElements, ICollection currentElements, string entityName, ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
// short-circuit(s)
if (currentElements.Count == 0)
{
// no new elements, the old list contains only Orphans
return oldElements;
}
if (oldElements.Count == 0)
{
// no old elements, so no Orphans neither
return oldElements;
}

IType idType = session.Factory.GetEntityPersister(entityName).IdentifierType;

// create the collection holding the orphans
List<object> res = new List<object>();

// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
var currentIds = new HashSet<TypedValue>();
foreach (object current in currentElements)
{
if (current != null && await (ForeignKeys.IsNotTransientSlowAsync(entityName, current, session, cancellationToken)).ConfigureAwait(false))
{
object currentId = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(entityName, current, session, cancellationToken)).ConfigureAwait(false);
currentIds.Add(new TypedValue(idType, currentId, false));
}
}

// iterate over the *old* list
foreach (object old in oldElements)
{
object oldId = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(entityName, old, session, cancellationToken)).ConfigureAwait(false);
if (!currentIds.Contains(new TypedValue(idType, oldId, false)))
{
res.Add(old);
}
}

return res;
}

// Since 5.3
[Obsolete("This method has no more usages and will be removed in a future version")]
public async Task IdentityRemoveAsync(IList list, object obj, string entityName, ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ public override Task<IEnumerable> GetDeletesAsync(ICollectionPersister persister
return Task.FromException<IEnumerable>(ex);
}
}


// Since 5.3
[Obsolete("This method has no more usages and will be removed in a future version")]
public override Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
Expand All @@ -85,7 +87,7 @@ public override Task<ICollection> GetOrphansAsync(object snapshot, string entity
try
{
var sn = (ICollection) snapshot;
return GetOrphansAsync(sn, (ICollection) _gbag, entityName, Session, cancellationToken);
return Task.FromResult<ICollection>(GetOrphans(sn, (ICollection) _gbag, entityName, Session));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ public override async Task<object> ReadFromAsync(DbDataReader reader, ICollectio
}
return element;
}


// Since 5.3
[Obsolete("This method has no more usages and will be removed in a future version")]
public override Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
Expand All @@ -165,7 +167,7 @@ public override Task<ICollection> GetOrphansAsync(object snapshot, string entity
try
{
var sn = (ISet<SnapshotElement>)GetSnapshot();
return GetOrphansAsync(sn.ToArray(x => x.Value), (ICollection) _values, entityName, Session, cancellationToken);
return Task.FromResult<ICollection>(GetOrphans(sn.ToArray(x => x.Value), (ICollection) _values, entityName, Session));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ namespace NHibernate.Collection.Generic
using System.Threading;
public partial class PersistentGenericList<T> : AbstractPersistentCollection, IList<T>, IList, IQueryable<T>
{


// Since 5.3
[Obsolete("This method has no more usages and will be removed in a future version")]
public override Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
Expand All @@ -39,7 +41,7 @@ public override Task<ICollection> GetOrphansAsync(object snapshot, string entity
try
{
var sn = (IList<T>)snapshot;
return GetOrphansAsync((ICollection)sn, (ICollection) WrappedList, entityName, Session, cancellationToken);
return Task.FromResult<ICollection>(GetOrphans((ICollection)sn, (ICollection) WrappedList, entityName, Session));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ namespace NHibernate.Collection.Generic
using System.Threading;
public partial class PersistentGenericMap<TKey, TValue> : AbstractPersistentCollection, IDictionary<TKey, TValue>, ICollection
{


// Since 5.3
[Obsolete("This method has no more usages and will be removed in a future version")]
public override Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
Expand All @@ -39,7 +41,7 @@ public override Task<ICollection> GetOrphansAsync(object snapshot, string entity
try
{
var sn = (IDictionary<TKey, TValue>) snapshot;
return GetOrphansAsync((ICollection)sn.Values, (ICollection)WrappedMap.Values, entityName, Session, cancellationToken);
return Task.FromResult<ICollection>(GetOrphans((ICollection)sn.Values, (ICollection)WrappedMap.Values, entityName, Session));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ namespace NHibernate.Collection.Generic
using System.Threading;
public partial class PersistentGenericSet<T> : AbstractPersistentCollection, ISet<T>, IQueryable<T>
{


// Since 5.3
[Obsolete("This method has no more usages and will be removed in a future version")]
public override Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
Expand All @@ -44,7 +46,7 @@ public override Task<ICollection> GetOrphansAsync(object snapshot, string entity
// TODO: Avoid duplicating shortcuts and array copy, by making base class GetOrphans() more flexible
if (WrappedSet.Count == 0) return Task.FromResult<ICollection>(sn);
if (((ICollection)sn).Count == 0) return Task.FromResult<ICollection>(sn);
return GetOrphansAsync(sn, WrappedSet.ToArray(), entityName, Session, cancellationToken);
return Task.FromResult<ICollection>(GetOrphans(sn, WrappedSet.ToArray(), entityName, Session));
}
catch (Exception ex)
{
Expand Down
20 changes: 2 additions & 18 deletions src/NHibernate/Async/Collection/IPersistentCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
using System;
using System.Collections;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using NHibernate.Collection.Generic;
using NHibernate.Engine;
using NHibernate.Loader;
Expand All @@ -19,8 +21,6 @@

namespace NHibernate.Collection
{
using System.Threading.Tasks;
using System.Threading;
public partial interface IPersistentCollection
{

Expand Down Expand Up @@ -92,27 +92,11 @@ public partial interface IPersistentCollection
/// </summary>
Task<IEnumerable> GetDeletesAsync(ICollectionPersister persister, bool indexIsFormula, CancellationToken cancellationToken);

/// <summary> Get the "queued" orphans</summary>
Task<ICollection> GetQueuedOrphansAsync(string entityName, CancellationToken cancellationToken);

/// <summary>
/// Called before inserting rows, to ensure that any surrogate keys are fully generated
/// </summary>
/// <param name="persister"></param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
Task PreInsertAsync(ICollectionPersister persister, CancellationToken cancellationToken);

/// <summary>
/// Get all "orphaned" elements
/// </summary>
/// <param name="snapshot">The snapshot of the collection.</param>
/// <param name="entityName">The persistent class whose objects
/// the collection is expected to contain.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
/// <returns>
/// An <see cref="ICollection"/> that contains all of the elements
/// that have been orphaned.
/// </returns>
Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken);
}
}
25 changes: 15 additions & 10 deletions src/NHibernate/Async/Collection/PersistentArrayHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,23 @@ namespace NHibernate.Collection
using System.Threading;
public partial class PersistentArrayHolder : AbstractPersistentCollection, ICollection
{

public override async Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken)

// Since 5.3
[Obsolete("This method has no more usages and will be removed in a future version")]
public override Task<ICollection> GetOrphansAsync(object snapshot, string entityName, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
object[] sn = (object[]) snapshot;
object[] arr = (object[]) array;
List<object> result = new List<object>(sn);
for (int i = 0; i < sn.Length; i++)
if (cancellationToken.IsCancellationRequested)
{
await (IdentityRemoveAsync(result, arr[i], entityName, Session, cancellationToken)).ConfigureAwait(false);
return Task.FromCanceled<ICollection>(cancellationToken);
}
try
{
return Task.FromResult<ICollection>(GetOrphans((object[]) snapshot, (object[]) array, entityName, Session));
}
catch (Exception ex)
{
return Task.FromException<ICollection>(ex);
}
return result;
}

public override async Task<bool> EqualsSnapshotAsync(ICollectionPersister persister, CancellationToken cancellationToken)
Expand Down Expand Up @@ -147,4 +152,4 @@ public override async Task<bool> NeedsUpdatingAsync(object entry, int i, IType e
&& await (elemType.IsDirtyAsync(array.GetValue(i), sn.GetValue(i), Session, cancellationToken)).ConfigureAwait(false);
}
}
}
}
4 changes: 2 additions & 2 deletions src/NHibernate/Async/Engine/Cascade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,11 @@ private async Task DeleteOrphansAsync(string entityName, IPersistentCollection p
if (pc.WasInitialized)
{
CollectionEntry ce = eventSource.PersistenceContext.GetCollectionEntry(pc);
orphans = ce == null ? CollectionHelper.EmptyCollection : await (ce.GetOrphansAsync(entityName, pc, cancellationToken)).ConfigureAwait(false);
orphans = ce == null ? CollectionHelper.EmptyCollection : ce.GetOrphans(entityName, pc);
}
else
{
orphans = await (pc.GetQueuedOrphansAsync(entityName, cancellationToken)).ConfigureAwait(false);
orphans = pc.GetQueuedOrphans(entityName);
}

foreach (object orphan in orphans)
Expand Down
17 changes: 2 additions & 15 deletions src/NHibernate/Async/Engine/CollectionEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@

using System;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using NHibernate.Action;
using NHibernate.Collection;
using NHibernate.Impl;
using NHibernate.Persister.Collection;

namespace NHibernate.Engine
{
using System.Threading.Tasks;
using System.Threading;
public partial class CollectionEntry
{

Expand Down Expand Up @@ -70,18 +70,5 @@ public async Task PreFlushAsync(IPersistentCollection collection, CancellationTo
reached = false;
processed = false;
}

public Task<ICollection> GetOrphansAsync(string entityName, IPersistentCollection collection, CancellationToken cancellationToken)
{
if (snapshot == null)
{
throw new AssertionFailure("no collection snapshot for orphan delete");
}
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<ICollection>(cancellationToken);
}
return collection.GetOrphansAsync(snapshot, entityName, cancellationToken);
}
}
}
14 changes: 0 additions & 14 deletions src/NHibernate/Async/Engine/ForeignKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,6 @@ private async Task<bool> IsNullifiableAsync(string entityName, object obj, Cance
public static async Task<bool> IsNotTransientSlowAsync(string entityName, object entity, ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (entity.IsProxy())
return true;
if (session.PersistenceContext.IsEntryFor(entity))
return true;
return !await (IsTransientSlowAsync(entityName, entity, session, cancellationToken)).ConfigureAwait(false);
}

Expand Down Expand Up @@ -271,16 +267,6 @@ public static async Task<object> GetEntityIdentifierIfNotUnsavedAsync(string ent

if ((await (IsTransientFastAsync(entityName, entity, session, cancellationToken)).ConfigureAwait(false)).GetValueOrDefault())
{
/***********************************************/
// TODO NH verify the behavior of NH607 test
// these lines are only to pass test NH607 during PersistenceContext porting
// i'm not secure that NH607 is a test for a right behavior
EntityEntry entry = session.PersistenceContext.GetEntry(entity);
if (entry != null)
return entry.Id;
// the check was put here to have les possible impact
/**********************************************/

entityName = entityName ?? session.GuessEntityName(entity);
string entityString = entity.ToString();
throw new TransientObjectException(
Expand Down
Loading