Skip to content

Commit fca2fe9

Browse files
authored
Remove redundant collection BeforeAssemble call from query cache (#3410)
1 parent 509ed76 commit fca2fe9

14 files changed

+246
-48
lines changed

src/NHibernate.Test/Async/CacheTest/BatchableCacheFixture.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,54 @@ public async Task CollectionLazyInitializationFromCacheIsBatchedAsync()
15881588
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
15891589
}
15901590

1591+
[Test]
1592+
public async Task CollectionLazyInitializationFromCacheIsBatched_FillCacheByQueryCacheAsync()
1593+
{
1594+
var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName);
1595+
var itemCache = (BatchableCache) itemPersister.Cache.Cache;
1596+
itemCache.ClearStatistics();
1597+
int id;
1598+
using (var s = OpenSession())
1599+
{
1600+
id = await (s.Query<ReadOnly>().Select(x => x.Id).FirstAsync());
1601+
var readOnly = (await (s.Query<ReadOnly>().Fetch(x => x.Items)
1602+
.Where(x => x.Id == id)
1603+
.WithOptions(x => x.SetCacheable(true))
1604+
.ToListAsync()))
1605+
.First();
1606+
Assert.That(itemCache.PutMultipleCalls.Count, Is.EqualTo(1));
1607+
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(0));
1608+
Assert.That(NHibernateUtil.IsInitialized(readOnly.Items));
1609+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1610+
}
1611+
1612+
itemCache.ClearStatistics();
1613+
using (var s = OpenSession())
1614+
{
1615+
var readOnly = (await (s.Query<ReadOnly>().Fetch(x => x.Items)
1616+
.Where(x => x.Id == id)
1617+
.WithOptions(x => x.SetCacheable(true))
1618+
.ToListAsync()))
1619+
.First();
1620+
Assert.That(itemCache.PutMultipleCalls.Count, Is.EqualTo(0));
1621+
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(1));
1622+
Assert.That(NHibernateUtil.IsInitialized(readOnly.Items));
1623+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1624+
}
1625+
1626+
itemCache.ClearStatistics();
1627+
1628+
1629+
using (var s = OpenSession())
1630+
{
1631+
var readOnly = await (s.GetAsync<ReadOnly>(id));
1632+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1633+
}
1634+
1635+
// 6 items with batch-size = 4 so 2 GetMany calls are expected 1st call: 4 items + 2nd call: 2 items
1636+
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
1637+
}
1638+
15911639
private async Task AssertMultipleCacheCallsAsync<TEntity>(IEnumerable<int> loadIds, IReadOnlyList<int> getIds, int idIndex,
15921640
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null, CancellationToken cancellationToken = default(CancellationToken))
15931641
where TEntity : CacheEntity

src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,6 +1576,54 @@ public void CollectionLazyInitializationFromCacheIsBatched()
15761576
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
15771577
}
15781578

1579+
[Test]
1580+
public void CollectionLazyInitializationFromCacheIsBatched_FillCacheByQueryCache()
1581+
{
1582+
var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName);
1583+
var itemCache = (BatchableCache) itemPersister.Cache.Cache;
1584+
itemCache.ClearStatistics();
1585+
int id;
1586+
using (var s = OpenSession())
1587+
{
1588+
id = s.Query<ReadOnly>().Select(x => x.Id).First();
1589+
var readOnly = s.Query<ReadOnly>().Fetch(x => x.Items)
1590+
.Where(x => x.Id == id)
1591+
.WithOptions(x => x.SetCacheable(true))
1592+
.ToList()
1593+
.First();
1594+
Assert.That(itemCache.PutMultipleCalls.Count, Is.EqualTo(1));
1595+
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(0));
1596+
Assert.That(NHibernateUtil.IsInitialized(readOnly.Items));
1597+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1598+
}
1599+
1600+
itemCache.ClearStatistics();
1601+
using (var s = OpenSession())
1602+
{
1603+
var readOnly = s.Query<ReadOnly>().Fetch(x => x.Items)
1604+
.Where(x => x.Id == id)
1605+
.WithOptions(x => x.SetCacheable(true))
1606+
.ToList()
1607+
.First();
1608+
Assert.That(itemCache.PutMultipleCalls.Count, Is.EqualTo(0));
1609+
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(1));
1610+
Assert.That(NHibernateUtil.IsInitialized(readOnly.Items));
1611+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1612+
}
1613+
1614+
itemCache.ClearStatistics();
1615+
1616+
1617+
using (var s = OpenSession())
1618+
{
1619+
var readOnly = s.Get<ReadOnly>(id);
1620+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1621+
}
1622+
1623+
// 6 items with batch-size = 4 so 2 GetMany calls are expected 1st call: 4 items + 2nd call: 2 items
1624+
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
1625+
}
1626+
15791627
private void AssertMultipleCacheCalls<TEntity>(IEnumerable<int> loadIds, IReadOnlyList<int> getIds, int idIndex,
15801628
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null)
15811629
where TEntity : CacheEntity

src/NHibernate/Async/Collection/Generic/PersistentGenericBag.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
111111
BeforeInitialize(persister, size);
112112

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

119116
for (var i = 0; i < size; i++)
120117
{
@@ -126,6 +123,18 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
126123
}
127124
}
128125

126+
private async Task BeforeAssembleAsync(IType elementType, object[] array, CancellationToken cancellationToken)
127+
{
128+
cancellationToken.ThrowIfCancellationRequested();
129+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
130+
return;
131+
132+
for (int i = 0; i < array.Length; i++)
133+
{
134+
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
135+
}
136+
}
137+
129138
public override Task<bool> NeedsInsertingAsync(object entry, int i, IType elemType, CancellationToken cancellationToken)
130139
{
131140
if (cancellationToken.IsCancellationRequested)

src/NHibernate/Async/Collection/Generic/PersistentGenericIdentifierBag.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
4747

4848
var identifierType = persister.IdentifierType;
4949
var elementType = persister.ElementType;
50-
for (int i = 0; i < size; i += 2)
51-
{
52-
await (identifierType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
53-
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
54-
}
50+
await (BeforeAssembleAsync(identifierType, elementType, array, cancellationToken)).ConfigureAwait(false);
5551

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

59+
private async Task BeforeAssembleAsync(IType identifierType, IType elementType, object[] array, CancellationToken cancellationToken)
60+
{
61+
cancellationToken.ThrowIfCancellationRequested();
62+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
63+
return;
64+
65+
for (int i = 0; i < array.Length; i += 2)
66+
{
67+
await (identifierType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
68+
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
69+
}
70+
}
71+
6372
public override async Task<object> DisassembleAsync(ICollectionPersister persister, CancellationToken cancellationToken)
6473
{
6574
cancellationToken.ThrowIfCancellationRequested();

src/NHibernate/Async/Collection/Generic/PersistentGenericList.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
100100
BeforeInitialize(persister, size);
101101

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

108105
for (int i = 0; i < size; i++)
109106
{
@@ -112,6 +109,18 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
112109
}
113110
}
114111

112+
private async Task BeforeAssembleAsync(IType elementType, object[] array, CancellationToken cancellationToken)
113+
{
114+
cancellationToken.ThrowIfCancellationRequested();
115+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
116+
return;
117+
118+
for (int i = 0; i < array.Length; i++)
119+
{
120+
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
121+
}
122+
}
123+
115124
public override async Task<object> DisassembleAsync(ICollectionPersister persister, CancellationToken cancellationToken)
116125
{
117126
cancellationToken.ThrowIfCancellationRequested();

src/NHibernate/Async/Collection/Generic/PersistentGenericMap.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
9797

9898
var indexType = persister.IndexType;
9999
var elementType = persister.ElementType;
100-
for (int i = 0; i < size; i += 2)
101-
{
102-
await (indexType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
103-
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
104-
}
100+
await (BeforeAssembleAsync(indexType, elementType, array, cancellationToken)).ConfigureAwait(false);
105101

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

109+
private async Task BeforeAssembleAsync(IType indexType, IType elementType, object[] array, CancellationToken cancellationToken)
110+
{
111+
cancellationToken.ThrowIfCancellationRequested();
112+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
113+
return;
114+
115+
for (int i = 0; i < array.Length; i += 2)
116+
{
117+
await (indexType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
118+
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
119+
}
120+
}
121+
113122
public override async Task<object> DisassembleAsync(ICollectionPersister persister, CancellationToken cancellationToken)
114123
{
115124
cancellationToken.ThrowIfCancellationRequested();

src/NHibernate/Async/Collection/Generic/PersistentGenericSet.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,7 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
8686
BeforeInitialize(persister, size);
8787

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

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

102+
private async Task BeforeAssembleAsync(IType elementType, object[] array, CancellationToken cancellationToken)
103+
{
104+
cancellationToken.ThrowIfCancellationRequested();
105+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
106+
return;
107+
108+
for (int i = 0; i < array.Length; i++)
109+
{
110+
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
111+
}
112+
}
113+
105114
public override async Task<object> ReadFromAsync(DbDataReader rs, ICollectionPersister role, ICollectionAliases descriptor, object owner, CancellationToken cancellationToken)
106115
{
107116
cancellationToken.ThrowIfCancellationRequested();

src/NHibernate/Async/Collection/PersistentArrayHolder.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,23 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
9595
array = System.Array.CreateInstance(persister.ElementClass, cached.Length);
9696

9797
var elementType = persister.ElementType;
98+
await (BeforeAssembleAsync(elementType, cached, cancellationToken)).ConfigureAwait(false);
99+
98100
for (int i = 0; i < cached.Length; i++)
99101
{
100-
await (elementType.BeforeAssembleAsync(cached[i], Session, cancellationToken)).ConfigureAwait(false);
102+
array.SetValue(await (elementType.AssembleAsync(cached[i], Session, owner, cancellationToken)).ConfigureAwait(false), i);
101103
}
104+
}
105+
106+
private async Task BeforeAssembleAsync(IType elementType, object[] cached, CancellationToken cancellationToken)
107+
{
108+
cancellationToken.ThrowIfCancellationRequested();
109+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
110+
return;
102111

103112
for (int i = 0; i < cached.Length; i++)
104113
{
105-
array.SetValue(await (elementType.AssembleAsync(cached[i], Session, owner, cancellationToken)).ConfigureAwait(false), i);
114+
await (elementType.BeforeAssembleAsync(cached[i], Session, cancellationToken)).ConfigureAwait(false);
106115
}
107116
}
108117

src/NHibernate/Collection/Generic/PersistentGenericBag.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -402,10 +402,7 @@ public override void InitializeFromCache(ICollectionPersister persister, object
402402
BeforeInitialize(persister, size);
403403

404404
var elementType = persister.ElementType;
405-
for (int i = 0; i < size; i++)
406-
{
407-
elementType.BeforeAssemble(array[i], Session);
408-
}
405+
BeforeAssemble(elementType, array);
409406

410407
for (var i = 0; i < size; i++)
411408
{
@@ -417,6 +414,17 @@ public override void InitializeFromCache(ICollectionPersister persister, object
417414
}
418415
}
419416

417+
private void BeforeAssemble(IType elementType, object[] array)
418+
{
419+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
420+
return;
421+
422+
for (int i = 0; i < array.Length; i++)
423+
{
424+
elementType.BeforeAssemble(array[i], Session);
425+
}
426+
}
427+
420428
public override bool IsSnapshotEmpty(object snapshot)
421429
{
422430
return ((ICollection) snapshot).Count == 0;

src/NHibernate/Collection/Generic/PersistentGenericIdentifierBag.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,7 @@ public override void InitializeFromCache(ICollectionPersister persister, object
7878

7979
var identifierType = persister.IdentifierType;
8080
var elementType = persister.ElementType;
81-
for (int i = 0; i < size; i += 2)
82-
{
83-
identifierType.BeforeAssemble(array[i], Session);
84-
elementType.BeforeAssemble(array[i + 1], Session);
85-
}
81+
BeforeAssemble(identifierType, elementType, array);
8682

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

90+
private void BeforeAssemble(IType identifierType, IType elementType, object[] array)
91+
{
92+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
93+
return;
94+
95+
for (int i = 0; i < array.Length; i += 2)
96+
{
97+
identifierType.BeforeAssemble(array[i], Session);
98+
elementType.BeforeAssemble(array[i + 1], Session);
99+
}
100+
}
101+
94102
private object GetIdentifier(int index)
95103
{
96104
// NH specific : To emulate IDictionary behavior but using Dictionary<int, object> (without boxing/unboxing for index)

src/NHibernate/Collection/Generic/PersistentGenericList.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,7 @@ public override void InitializeFromCache(ICollectionPersister persister, object
162162
BeforeInitialize(persister, size);
163163

164164
var elementType = persister.ElementType;
165-
for (int i = 0; i < size; i++)
166-
{
167-
elementType.BeforeAssemble(array[i], Session);
168-
}
165+
BeforeAssemble(elementType, array);
169166

170167
for (int i = 0; i < size; i++)
171168
{
@@ -174,6 +171,17 @@ public override void InitializeFromCache(ICollectionPersister persister, object
174171
}
175172
}
176173

174+
private void BeforeAssemble(IType elementType, object[] array)
175+
{
176+
if (Session.PersistenceContext.BatchFetchQueue.QueryCacheQueue != null)
177+
return;
178+
179+
for (int i = 0; i < array.Length; i++)
180+
{
181+
elementType.BeforeAssemble(array[i], Session);
182+
}
183+
}
184+
177185
public override object Disassemble(ICollectionPersister persister)
178186
{
179187
int length = WrappedList.Count;

0 commit comments

Comments
 (0)