Skip to content

Commit 38c3c53

Browse files
Implement multiple get and put for query cache and query batch
1 parent 25777a3 commit 38c3c53

27 files changed

+2330
-596
lines changed

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

Lines changed: 256 additions & 57 deletions
Large diffs are not rendered by default.

src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs

Lines changed: 255 additions & 56 deletions
Large diffs are not rendered by default.

src/NHibernate/Async/Cache/CacheBatcher.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@
88
//------------------------------------------------------------------------------
99

1010

11-
using System;
12-
using System.Collections.Generic;
1311
using System.Diagnostics;
14-
using System.Text;
15-
using NHibernate.Cache.Access;
1612
using NHibernate.Engine;
1713
using NHibernate.Persister.Collection;
1814
using NHibernate.Persister.Entity;
@@ -21,7 +17,7 @@ namespace NHibernate.Cache
2117
{
2218
using System.Threading.Tasks;
2319
using System.Threading;
24-
internal partial class CacheBatcher
20+
public sealed partial class CacheBatcher
2521
{
2622

2723
/// <summary>
@@ -31,7 +27,7 @@ internal partial class CacheBatcher
3127
/// <param name="persister">The entity persister.</param>
3228
/// <param name="data">The data to put in the cache.</param>
3329
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
34-
public async Task AddToBatchAsync(IEntityPersister persister, CachePutData data, CancellationToken cancellationToken)
30+
internal async Task AddToBatchAsync(IEntityPersister persister, CachePutData data, CancellationToken cancellationToken)
3531
{
3632
cancellationToken.ThrowIfCancellationRequested();
3733
if (ShouldExecuteBatch(persister, _putBatch))
@@ -54,7 +50,7 @@ public async Task AddToBatchAsync(IEntityPersister persister, CachePutData data,
5450
/// <param name="persister">The collection persister.</param>
5551
/// <param name="data">The data to put in the cache.</param>
5652
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
57-
public async Task AddToBatchAsync(ICollectionPersister persister, CachePutData data, CancellationToken cancellationToken)
53+
internal async Task AddToBatchAsync(ICollectionPersister persister, CachePutData data, CancellationToken cancellationToken)
5854
{
5955
cancellationToken.ThrowIfCancellationRequested();
6056
if (ShouldExecuteBatch(persister, _putBatch))
@@ -74,7 +70,7 @@ public async Task AddToBatchAsync(ICollectionPersister persister, CachePutData d
7470
/// Executes the current batch.
7571
/// </summary>
7672
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
77-
public async Task ExecuteBatchAsync(CancellationToken cancellationToken)
73+
internal async Task ExecuteBatchAsync(CancellationToken cancellationToken)
7874
{
7975
cancellationToken.ThrowIfCancellationRequested();
8076
if (_currentBatch == null || _currentBatch.BatchSize == 0)

src/NHibernate/Async/Cache/IQueryCache.cs

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//------------------------------------------------------------------------------
99

1010

11+
using System;
1112
using System.Collections;
1213
using System.Collections.Generic;
1314
using NHibernate.Engine;
@@ -20,8 +21,273 @@ namespace NHibernate.Cache
2021
public partial interface IQueryCache
2122
{
2223

24+
/// <summary>
25+
/// Clear the cache.
26+
/// </summary>
27+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
2328
Task ClearAsync(CancellationToken cancellationToken);
29+
// Since 5.2
30+
[Obsolete("Have the query cache implement IBatchableQueryCache, and use IBatchableQueryCache.Put")]
2431
Task<bool> PutAsync(QueryKey key, ICacheAssembler[] returnTypes, IList result, bool isNaturalKeyLookup, ISessionImplementor session, CancellationToken cancellationToken);
32+
// Since 5.2
33+
[Obsolete("Have the query cache implement IBatchableQueryCache, and use IBatchableQueryCache.Get")]
2534
Task<IList> GetAsync(QueryKey key, ICacheAssembler[] returnTypes, bool isNaturalKeyLookup, ISet<string> spaces, ISessionImplementor session, CancellationToken cancellationToken);
2635
}
36+
37+
public partial interface IBatchableQueryCache : IQueryCache
38+
{
39+
/// <summary>
40+
/// Get query results from the cache.
41+
/// </summary>
42+
/// <param name="key">The query key.</param>
43+
/// <param name="queryParameters">The query parameters.</param>
44+
/// <param name="returnTypes">The query result row types.</param>
45+
/// <param name="spaces">The query spaces.</param>
46+
/// <param name="session">The session for which the query is executed.</param>
47+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
48+
/// <returns>The query results, if cached.</returns>
49+
Task<IList> GetAsync(
50+
QueryKey key, QueryParameters queryParameters, ICacheAssembler[] returnTypes, ISet<string> spaces,
51+
ISessionImplementor session, CancellationToken cancellationToken);
52+
53+
/// <summary>
54+
/// Put query results in the cache.
55+
/// </summary>
56+
/// <param name="key">The query key.</param>
57+
/// <param name="queryParameters">The query parameters.</param>
58+
/// <param name="returnTypes">The query result row types.</param>
59+
/// <param name="result">The query result.</param>
60+
/// <param name="session">The session for which the query was executed.</param>
61+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
62+
/// <returns><see langword="true" /> if the result has been cached, <see langword="false" />
63+
/// otherwise.</returns>
64+
Task<bool> PutAsync(
65+
QueryKey key, QueryParameters queryParameters, ICacheAssembler[] returnTypes, IList result,
66+
ISessionImplementor session, CancellationToken cancellationToken);
67+
68+
/// <summary>
69+
/// Retrieve multiple query results from the cache.
70+
/// </summary>
71+
/// <param name="keys">The query keys.</param>
72+
/// <param name="queryParameters">The array of query parameters matching <paramref name="keys"/>.</param>
73+
/// <param name="returnTypes">The array of query result row types matching <paramref name="keys"/>.</param>
74+
/// <param name="spaces">The array of query spaces matching <paramref name="keys"/>.</param>
75+
/// <param name="session">The session for which the queries are executed.</param>
76+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
77+
/// <returns>The cached query results, matching each key of <paramref name="keys"/> respectively. For each
78+
/// missed key, it will contain a <see langword="null" />.</returns>
79+
Task<IList[]> GetManyAsync(
80+
QueryKey[] keys, QueryParameters[] queryParameters, ICacheAssembler[][] returnTypes,
81+
ISet<string>[] spaces, ISessionImplementor session, CancellationToken cancellationToken);
82+
83+
/// <summary>
84+
/// Attempt to cache objects, after loading them from the database.
85+
/// </summary>
86+
/// <param name="keys">The query keys.</param>
87+
/// <param name="queryParameters">The array of query parameters matching <paramref name="keys"/>.</param>
88+
/// <param name="returnTypes">The array of query result row types matching <paramref name="keys"/>.</param>
89+
/// <param name="results">The array of query results matching <paramref name="keys"/>.</param>
90+
/// <param name="session">The session for which the queries were executed.</param>
91+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
92+
/// <returns>An array of boolean indicating if each query was successfully cached.</returns>
93+
/// <exception cref="CacheException"></exception>
94+
Task<bool[]> PutManyAsync(
95+
QueryKey[] keys, QueryParameters[] queryParameters, ICacheAssembler[][] returnTypes, IList[] results,
96+
ISessionImplementor session, CancellationToken cancellationToken);
97+
}
98+
99+
internal static partial class QueryCacheExtensions
100+
{
101+
102+
/// <summary>
103+
/// Get query results from the cache.
104+
/// </summary>
105+
/// <param name="queryCache">The cache.</param>
106+
/// <param name="key">The query key.</param>
107+
/// <param name="queryParameters">The query parameters.</param>
108+
/// <param name="returnTypes">The query result row types.</param>
109+
/// <param name="spaces">The query spaces.</param>
110+
/// <param name="session">The session for which the query is executed.</param>
111+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
112+
/// <returns>The query results, if cached.</returns>
113+
public static async Task<IList> GetAsync(
114+
this IQueryCache queryCache,
115+
QueryKey key,
116+
QueryParameters queryParameters,
117+
ICacheAssembler[] returnTypes,
118+
ISet<string> spaces,
119+
ISessionImplementor session, CancellationToken cancellationToken)
120+
{
121+
cancellationToken.ThrowIfCancellationRequested();
122+
if (queryCache is IBatchableQueryCache batchableQueryCache)
123+
{
124+
return await (batchableQueryCache.GetAsync(
125+
key,
126+
queryParameters,
127+
returnTypes,
128+
spaces,
129+
session, cancellationToken)).ConfigureAwait(false);
130+
}
131+
132+
if (!_hasWarnForObsoleteQueryCache)
133+
{
134+
_hasWarnForObsoleteQueryCache = true;
135+
Log.Warn("{0} is obsolete, it should implement {1}", queryCache, nameof(IBatchableQueryCache));
136+
}
137+
138+
var persistenceContext = session.PersistenceContext;
139+
140+
var defaultReadOnlyOrig = persistenceContext.DefaultReadOnly;
141+
142+
if (queryParameters.IsReadOnlyInitialized)
143+
persistenceContext.DefaultReadOnly = queryParameters.ReadOnly;
144+
else
145+
queryParameters.ReadOnly = persistenceContext.DefaultReadOnly;
146+
147+
try
148+
{
149+
#pragma warning disable 618
150+
return await (queryCache.GetAsync(
151+
#pragma warning restore 618
152+
key,
153+
returnTypes,
154+
queryParameters.NaturalKeyLookup,
155+
spaces,
156+
session, cancellationToken)).ConfigureAwait(false);
157+
}
158+
finally
159+
{
160+
persistenceContext.DefaultReadOnly = defaultReadOnlyOrig;
161+
}
162+
}
163+
164+
/// <summary>
165+
/// Put query results in the cache.
166+
/// </summary>
167+
/// <param name="queryCache">The cache.</param>
168+
/// <param name="key">The query key.</param>
169+
/// <param name="queryParameters">The query parameters.</param>
170+
/// <param name="returnTypes">The query result row types.</param>
171+
/// <param name="result">The query result.</param>
172+
/// <param name="session">The session for which the query was executed.</param>
173+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
174+
/// <returns><see langword="true" /> if the result has been cached, <see langword="false" />
175+
/// otherwise.</returns>
176+
public static Task<bool> PutAsync(
177+
this IQueryCache queryCache,
178+
QueryKey key,
179+
QueryParameters queryParameters,
180+
ICacheAssembler[] returnTypes,
181+
IList result,
182+
ISessionImplementor session, CancellationToken cancellationToken)
183+
{
184+
if (cancellationToken.IsCancellationRequested)
185+
{
186+
return Task.FromCanceled<bool>(cancellationToken);
187+
}
188+
try
189+
{
190+
if (queryCache is IBatchableQueryCache batchableQueryCache)
191+
{
192+
return batchableQueryCache.PutAsync(
193+
key, queryParameters,
194+
returnTypes,
195+
result, session, cancellationToken);
196+
}
197+
198+
#pragma warning disable 618
199+
return queryCache.PutAsync(
200+
#pragma warning restore 618
201+
key,
202+
returnTypes,
203+
result,
204+
queryParameters.NaturalKeyLookup,
205+
session, cancellationToken);
206+
}
207+
catch (Exception ex)
208+
{
209+
return Task.FromException<bool>(ex);
210+
}
211+
}
212+
213+
/// <summary>
214+
/// Retrieve multiple query results from the cache.
215+
/// </summary>
216+
/// <param name="queryCache">The cache.</param>
217+
/// <param name="keys">The query keys.</param>
218+
/// <param name="queryParameters">The array of query parameters matching <paramref name="keys"/>.</param>
219+
/// <param name="returnTypes">The array of query result row types matching <paramref name="keys"/>.</param>
220+
/// <param name="spaces">The array of query spaces matching <paramref name="keys"/>.</param>
221+
/// <param name="session">The session for which the queries are executed.</param>
222+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
223+
/// <returns>The cached query results, matching each key of <paramref name="keys"/> respectively. For each
224+
/// missed key, it will contain a <see langword="null" />.</returns>
225+
public static async Task<IList[]> GetManyAsync(
226+
this IQueryCache queryCache,
227+
QueryKey[] keys,
228+
QueryParameters[] queryParameters,
229+
ICacheAssembler[][] returnTypes,
230+
ISet<string>[] spaces,
231+
ISessionImplementor session, CancellationToken cancellationToken)
232+
{
233+
cancellationToken.ThrowIfCancellationRequested();
234+
if (queryCache is IBatchableQueryCache batchableQueryCache)
235+
{
236+
return await (batchableQueryCache.GetManyAsync(
237+
keys,
238+
queryParameters,
239+
returnTypes,
240+
spaces,
241+
session, cancellationToken)).ConfigureAwait(false);
242+
}
243+
244+
var results = new IList[keys.Length];
245+
for (var i = 0; i < keys.Length; i++)
246+
{
247+
results[i] = await (queryCache.GetAsync(keys[i], queryParameters[i], returnTypes[i], spaces[i], session, cancellationToken)).ConfigureAwait(false);
248+
}
249+
250+
return results;
251+
}
252+
253+
/// <summary>
254+
/// Attempt to cache objects, after loading them from the database.
255+
/// </summary>
256+
/// <param name="queryCache">The cache.</param>
257+
/// <param name="keys">The query keys.</param>
258+
/// <param name="queryParameters">The array of query parameters matching <paramref name="keys"/>.</param>
259+
/// <param name="returnTypes">The array of query result row types matching <paramref name="keys"/>.</param>
260+
/// <param name="results">The array of query results matching <paramref name="keys"/>.</param>
261+
/// <param name="session">The session for which the queries were executed.</param>
262+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
263+
/// <returns>An array of boolean indicating if each query was successfully cached.</returns>
264+
/// <exception cref="CacheException"></exception>
265+
public static async Task<bool[]> PutManyAsync(
266+
this IQueryCache queryCache,
267+
QueryKey[] keys,
268+
QueryParameters[] queryParameters,
269+
ICacheAssembler[][] returnTypes,
270+
IList[] results,
271+
ISessionImplementor session, CancellationToken cancellationToken)
272+
{
273+
cancellationToken.ThrowIfCancellationRequested();
274+
if (queryCache is IBatchableQueryCache batchableQueryCache)
275+
{
276+
return await (batchableQueryCache.PutManyAsync(
277+
keys,
278+
queryParameters,
279+
returnTypes,
280+
results,
281+
session, cancellationToken)).ConfigureAwait(false);
282+
}
283+
284+
var puts = new bool[keys.Length];
285+
for (var i = 0; i < keys.Length; i++)
286+
{
287+
puts[i] = await (queryCache.PutAsync(keys[i], queryParameters[i], returnTypes[i], results[i], session, cancellationToken)).ConfigureAwait(false);
288+
}
289+
290+
return puts;
291+
}
292+
}
27293
}

0 commit comments

Comments
 (0)