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 1 commit
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
116 changes: 15 additions & 101 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 @@ -92,41 +93,6 @@ 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 Down Expand Up @@ -154,79 +120,27 @@ public virtual Task PreInsertAsync(ICollectionPersister persister, CancellationT
/// </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)
public async Task IdentityRemoveAsync(IList list, object obj, 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;
if (obj == null || await (ForeignKeys.IsTransientSlowAsync(entityName, obj, session, cancellationToken)).ConfigureAwait(false))
return;

// create the collection holding the orphans
List<object> res = new List<object>();
var persister = session.Factory.GetEntityPersister(entityName);
IType idType = persister.IdentifierType;
object idToRemove = ForeignKeys.GetIdentifier(persister, obj);

// 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)
for (var index = list.Count - 1; index >= 0; index--)
{
if (current != null && await (ForeignKeys.IsNotTransientSlowAsync(entityName, current, session, cancellationToken)).ConfigureAwait(false))
object current = list[index];
if (current == null)
{
object currentId = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(entityName, current, session, cancellationToken)).ConfigureAwait(false);
currentIds.Add(new TypedValue(idType, currentId, false));
continue;
}
}

// 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;
}

public async Task IdentityRemoveAsync(IList list, object obj, string entityName, ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (obj != null && await (ForeignKeys.IsNotTransientSlowAsync(entityName, obj, session, cancellationToken)).ConfigureAwait(false))
{
IType idType = session.Factory.GetEntityPersister(entityName).IdentifierType;

object idOfCurrent = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(entityName, obj, session, cancellationToken)).ConfigureAwait(false);
List<object> toRemove = new List<object>(list.Count);
foreach (object current in list)
{
if (current == null)
{
continue;
}
object idOfOld = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(entityName, current, session, cancellationToken)).ConfigureAwait(false);
if (idType.IsEqual(idOfCurrent, idOfOld, session.Factory))
{
toRemove.Add(current);
}
}
foreach (object ro in toRemove)
if (obj == current || idType.IsEqual(idToRemove, ForeignKeys.GetIdentifier(persister, current), session.Factory))
{
list.Remove(ro);
list.RemoveAt(index);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,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(snapshot, entityName));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,7 @@ public override Task<ICollection> GetOrphansAsync(object snapshot, string entity
}
try
{
var sn = (ISet<SnapshotElement>)GetSnapshot();
return GetOrphansAsync(sn.Select(x => x.Value).ToArray(), (ICollection) _values, entityName, Session, cancellationToken);
return Task.FromResult<ICollection>(GetOrphans(snapshot, entityName));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,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(snapshot, entityName));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,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(snapshot, entityName));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,7 @@ public override Task<ICollection> GetOrphansAsync(object snapshot, string entity
}
try
{
var sn = new SetSnapShot<T>((IEnumerable<T>)snapshot);

// 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(snapshot, entityName));
}
catch (Exception ex)
{
Expand Down
7 changes: 2 additions & 5 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,9 +92,6 @@ 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>
Expand Down
2 changes: 1 addition & 1 deletion src/NHibernate/Async/Engine/Cascade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ private async Task DeleteOrphansAsync(string entityName, IPersistentCollection p
}
else
{
orphans = await (pc.GetQueuedOrphansAsync(entityName, cancellationToken)).ConfigureAwait(false);
orphans = pc.GetQueuedOrphans(entityName);
}

foreach (object orphan in orphans)
Expand Down
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
3 changes: 2 additions & 1 deletion src/NHibernate/Async/Type/IType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using NHibernate.Engine;
using NHibernate.SqlTypes;
Expand All @@ -17,7 +18,7 @@ namespace NHibernate.Type
{
using System.Threading.Tasks;
using System.Threading;

public partial interface IType : ICacheAssembler
{

Expand Down
Loading