Skip to content

Remove redundant collection BeforeAssemble call from query cache #3410

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 5 commits into from
Aug 16, 2023
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
48 changes: 48 additions & 0 deletions src/NHibernate.Test/Async/CacheTest/BatchableCacheFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,54 @@ public async Task CollectionLazyInitializationFromCacheIsBatchedAsync()
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
}

[Test]
public async Task CollectionLazyInitializationFromCacheIsBatched_FillCacheByQueryCacheAsync()
{
var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName);
var itemCache = (BatchableCache) itemPersister.Cache.Cache;
itemCache.ClearStatistics();
int id;
using (var s = OpenSession())
{
id = await (s.Query<ReadOnly>().Select(x => x.Id).FirstAsync());
var readOnly = (await (s.Query<ReadOnly>().Fetch(x => x.Items)
.Where(x => x.Id == id)
.WithOptions(x => x.SetCacheable(true))
.ToListAsync()))
.First();
Assert.That(itemCache.PutMultipleCalls.Count, Is.EqualTo(1));
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(0));
Assert.That(NHibernateUtil.IsInitialized(readOnly.Items));
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

itemCache.ClearStatistics();
using (var s = OpenSession())
{
var readOnly = (await (s.Query<ReadOnly>().Fetch(x => x.Items)
.Where(x => x.Id == id)
.WithOptions(x => x.SetCacheable(true))
.ToListAsync()))
.First();
Assert.That(itemCache.PutMultipleCalls.Count, Is.EqualTo(0));
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(1));
Assert.That(NHibernateUtil.IsInitialized(readOnly.Items));
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

itemCache.ClearStatistics();


using (var s = OpenSession())
{
var readOnly = await (s.GetAsync<ReadOnly>(id));
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

// 6 items with batch-size = 4 so 2 GetMany calls are expected 1st call: 4 items + 2nd call: 2 items
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
}

private async Task AssertMultipleCacheCallsAsync<TEntity>(IEnumerable<int> loadIds, IReadOnlyList<int> getIds, int idIndex,
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null, CancellationToken cancellationToken = default(CancellationToken))
where TEntity : CacheEntity
Expand Down
48 changes: 48 additions & 0 deletions src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1576,6 +1576,54 @@ public void CollectionLazyInitializationFromCacheIsBatched()
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
}

[Test]
public void CollectionLazyInitializationFromCacheIsBatched_FillCacheByQueryCache()
{
var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName);
var itemCache = (BatchableCache) itemPersister.Cache.Cache;
itemCache.ClearStatistics();
int id;
using (var s = OpenSession())
{
id = s.Query<ReadOnly>().Select(x => x.Id).First();
var readOnly = s.Query<ReadOnly>().Fetch(x => x.Items)
.Where(x => x.Id == id)
.WithOptions(x => x.SetCacheable(true))
.ToList()
.First();
Assert.That(itemCache.PutMultipleCalls.Count, Is.EqualTo(1));
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(0));
Assert.That(NHibernateUtil.IsInitialized(readOnly.Items));
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

itemCache.ClearStatistics();
using (var s = OpenSession())
{
var readOnly = s.Query<ReadOnly>().Fetch(x => x.Items)
.Where(x => x.Id == id)
.WithOptions(x => x.SetCacheable(true))
.ToList()
.First();
Assert.That(itemCache.PutMultipleCalls.Count, Is.EqualTo(0));
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(1));
Assert.That(NHibernateUtil.IsInitialized(readOnly.Items));
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

itemCache.ClearStatistics();


using (var s = OpenSession())
{
var readOnly = s.Get<ReadOnly>(id);
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
}

// 6 items with batch-size = 4 so 2 GetMany calls are expected 1st call: 4 items + 2nd call: 2 items
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
}

private void AssertMultipleCacheCalls<TEntity>(IEnumerable<int> loadIds, IReadOnlyList<int> getIds, int idIndex,
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null)
where TEntity : CacheEntity
Expand Down
17 changes: 13 additions & 4 deletions src/NHibernate/Async/Collection/Generic/PersistentGenericBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
BeforeInitialize(persister, size);

var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
await (BeforeAssembleAsync(elementType, array, cancellationToken)).ConfigureAwait(false);

for (var i = 0; i < size; i++)
{
Expand All @@ -126,6 +123,18 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
}
}

private async Task BeforeAssembleAsync(IType elementType, object[] array, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < array.Length; i++)
{
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
}

public override Task<bool> NeedsInsertingAsync(object entry, int i, IType elemType, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist

var identifierType = persister.IdentifierType;
var elementType = persister.ElementType;
for (int i = 0; i < size; i += 2)
{
await (identifierType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
}
await (BeforeAssembleAsync(identifierType, elementType, array, cancellationToken)).ConfigureAwait(false);

for (int i = 0; i < size; i += 2)
{
Expand All @@ -60,6 +56,19 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
}
}

private async Task BeforeAssembleAsync(IType identifierType, IType elementType, object[] array, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < array.Length; i += 2)
{
await (identifierType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
}
}

public override async Task<object> DisassembleAsync(ICollectionPersister persister, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down
17 changes: 13 additions & 4 deletions src/NHibernate/Async/Collection/Generic/PersistentGenericList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
BeforeInitialize(persister, size);

var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
await (BeforeAssembleAsync(elementType, array, cancellationToken)).ConfigureAwait(false);

for (int i = 0; i < size; i++)
{
Expand All @@ -112,6 +109,18 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
}
}

private async Task BeforeAssembleAsync(IType elementType, object[] array, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < array.Length; i++)
{
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
}

public override async Task<object> DisassembleAsync(ICollectionPersister persister, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down
19 changes: 14 additions & 5 deletions src/NHibernate/Async/Collection/Generic/PersistentGenericMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist

var indexType = persister.IndexType;
var elementType = persister.ElementType;
for (int i = 0; i < size; i += 2)
{
await (indexType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
}
await (BeforeAssembleAsync(indexType, elementType, array, cancellationToken)).ConfigureAwait(false);

for (int i = 0; i < size; i += 2)
{
Expand All @@ -110,6 +106,19 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
}
}

private async Task BeforeAssembleAsync(IType indexType, IType elementType, object[] array, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < array.Length; i += 2)
{
await (indexType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
}
}

public override async Task<object> DisassembleAsync(ICollectionPersister persister, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down
17 changes: 13 additions & 4 deletions src/NHibernate/Async/Collection/Generic/PersistentGenericSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
BeforeInitialize(persister, size);

var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
await (BeforeAssembleAsync(elementType, array, cancellationToken)).ConfigureAwait(false);

for (int i = 0; i < size; i++)
{
Expand All @@ -102,6 +99,18 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
SetInitialized();
}

private async Task BeforeAssembleAsync(IType elementType, object[] array, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < array.Length; i++)
{
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
}
}

public override async Task<object> ReadFromAsync(DbDataReader rs, ICollectionPersister role, ICollectionAliases descriptor, object owner, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down
13 changes: 11 additions & 2 deletions src/NHibernate/Async/Collection/PersistentArrayHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,23 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
array = System.Array.CreateInstance(persister.ElementClass, cached.Length);

var elementType = persister.ElementType;
await (BeforeAssembleAsync(elementType, cached, cancellationToken)).ConfigureAwait(false);

for (int i = 0; i < cached.Length; i++)
{
await (elementType.BeforeAssembleAsync(cached[i], Session, cancellationToken)).ConfigureAwait(false);
array.SetValue(await (elementType.AssembleAsync(cached[i], Session, owner, cancellationToken)).ConfigureAwait(false), i);
}
}

private async Task BeforeAssembleAsync(IType elementType, object[] cached, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < cached.Length; i++)
{
array.SetValue(await (elementType.AssembleAsync(cached[i], Session, owner, cancellationToken)).ConfigureAwait(false), i);
await (elementType.BeforeAssembleAsync(cached[i], Session, cancellationToken)).ConfigureAwait(false);
}
}

Expand Down
16 changes: 12 additions & 4 deletions src/NHibernate/Collection/Generic/PersistentGenericBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,7 @@ public override void InitializeFromCache(ICollectionPersister persister, object
BeforeInitialize(persister, size);

var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
elementType.BeforeAssemble(array[i], Session);
}
BeforeAssemble(elementType, array);

for (var i = 0; i < size; i++)
{
Expand All @@ -417,6 +414,17 @@ public override void InitializeFromCache(ICollectionPersister persister, object
}
}

private void BeforeAssemble(IType elementType, object[] array)
{
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < array.Length; i++)
{
elementType.BeforeAssemble(array[i], Session);
}
}

public override bool IsSnapshotEmpty(object snapshot)
{
return ((ICollection) snapshot).Count == 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,7 @@ public override void InitializeFromCache(ICollectionPersister persister, object

var identifierType = persister.IdentifierType;
var elementType = persister.ElementType;
for (int i = 0; i < size; i += 2)
{
identifierType.BeforeAssemble(array[i], Session);
elementType.BeforeAssemble(array[i + 1], Session);
}
BeforeAssemble(identifierType, elementType, array);

for (int i = 0; i < size; i += 2)
{
Expand All @@ -91,6 +87,18 @@ public override void InitializeFromCache(ICollectionPersister persister, object
}
}

private void BeforeAssemble(IType identifierType, IType elementType, object[] array)
{
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < array.Length; i += 2)
{
identifierType.BeforeAssemble(array[i], Session);
elementType.BeforeAssemble(array[i + 1], Session);
}
}

private object GetIdentifier(int index)
{
// NH specific : To emulate IDictionary behavior but using Dictionary<int, object> (without boxing/unboxing for index)
Expand Down
16 changes: 12 additions & 4 deletions src/NHibernate/Collection/Generic/PersistentGenericList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,7 @@ public override void InitializeFromCache(ICollectionPersister persister, object
BeforeInitialize(persister, size);

var elementType = persister.ElementType;
for (int i = 0; i < size; i++)
{
elementType.BeforeAssemble(array[i], Session);
}
BeforeAssemble(elementType, array);

for (int i = 0; i < size; i++)
{
Expand All @@ -174,6 +171,17 @@ public override void InitializeFromCache(ICollectionPersister persister, object
}
}

private void BeforeAssemble(IType elementType, object[] array)
{
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
return;

for (int i = 0; i < array.Length; i++)
{
elementType.BeforeAssemble(array[i], Session);
}
}

public override object Disassemble(ICollectionPersister persister)
{
int length = WrappedList.Count;
Expand Down
Loading