forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCacheBase.cs
396 lines (355 loc) · 12.1 KB
/
CacheBase.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
using System;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using NHibernate.Cache.Entry;
using NHibernate.Type;
namespace NHibernate.Cache
{
/// <summary>
/// Base class for letting implementors define a caching algorithm.
/// </summary>
/// <remarks>
/// <threadsafety instance="true" />
/// <para>
/// All implementations <em>must</em> be threadsafe.
/// </para>
/// <para>
/// The key is the identifier of the object that is being cached. The key is in most cases
/// a <see cref="CacheKey" />.
/// </para>
/// <para>
/// The value can be a <see cref="CacheEntry"/>, a <see cref="CollectionCacheEntry"/>,
/// a <see cref="AnyType.ObjectTypeCacheEntry"/>, an <see cref="IList"/> or
/// <see cref="IDictionary"/> implementation, all containing simple values or array of
/// simple values. It can also be directly a simple value or an array of simple values.
/// And it can be a <see cref="CachedItem"/> containing any of the previous types, or
/// a <see cref="CacheLock"/>.
/// </para>
/// <para>
/// All those types are binary serializable.
/// </para>
/// <para>
/// This base class provides minimal async method implementations delegating their work to their
/// synchronous counterparts. Override them for supplying actual async operations.
/// </para>
/// <para>
/// Similarly, this base class provides minimal multiple get/put/lock/unlock implementations
/// delegating their work to their single operation counterparts. Override them if your cache
/// implementation supports multiple operations.
/// </para>
/// </remarks>
public abstract partial class CacheBase :
// 6.0 TODO: remove ICache
#pragma warning disable 618
ICache
#pragma warning restore 618
{
/// <summary>
/// A reasonable "lock timeout".
/// </summary>
public abstract int Timeout { get; }
/// <summary>
/// The name of the cache region.
/// </summary>
public abstract string RegionName { get; }
/// <summary>
/// Should batched get operations be preferred other single get calls?
/// </summary>
/// <remarks>
/// <para>
/// <see cref="CacheBase"/> implementation always yield <c>false</c>, override it if required.
/// </para>
/// <para>
/// This property should yield <see langword="false" /> if <see cref="GetMany" /> delegates
/// its implementation to <see cref="Get"/>.
/// </para>
/// <para>
/// When <see langword="true" />, NHibernate will attempt to get other non initialized proxies or
/// collections from the cache instead of only getting the proxy or collection which initialization
/// is asked for. If this cache implementation does not benefit from batching together get operations,
/// this may result in a performance loss.
/// </para>
/// <para>
/// When <see langword="false" />, NHibernate will still call <see cref="GetMany" /> when it has many
/// gets to perform. Its <see cref="CacheBase" /> default implementation is adequate for this case.
/// </para>
/// </remarks>
public virtual bool PreferMultipleGet => false;
#region Basic abstract operations
/// <summary>
/// Get the item from the cache.
/// </summary>
/// <param name="key">The item key.</param>
/// <returns>The cached item.</returns>
public abstract object Get(object key);
/// <summary>
/// Put the item into the cache.
/// </summary>
/// <param name="key">The item key.</param>
/// <param name="value">The item.</param>
public abstract void Put(object key, object value);
/// <summary>
/// Remove an item from the cache.
/// </summary>
/// <param name="key">The item key.</param>
public abstract void Remove(object key);
/// <summary>
/// Clear the cache.
/// </summary>
public abstract void Clear();
/// <summary>
/// Clean up.
/// </summary>
public abstract void Destroy();
/// <summary>
/// Lock the item from being concurrently changed.
/// </summary>
/// <param name="key">The item key.</param>
/// <returns>A lock object to use for unlocking the item. Can be <see langword="null" />.</returns>
/// <remarks>The implementation is allowed to do nothing for non-clustered cache.</remarks>
public abstract object Lock(object key);
/// <summary>
/// Unlock an item which was previously locked.
/// </summary>
/// <param name="key">The item key.</param>
/// <param name="lockValue">The lock object to use for unlocking the item, as received from <see cref="Lock"/>.</param>
/// <remarks>The implementation should do nothing if <see cref="Lock"/> own implementation does nothing.</remarks>
public abstract void Unlock(object key, object lockValue);
/// <summary>
/// Generate a timestamp.
/// </summary>
/// <returns>A timestamp.</returns>
public abstract long NextTimestamp();
#endregion
#region Batch operations default implementation
/// <summary>
/// Get multiple items from the cache.
/// </summary>
/// <param name="keys">The keys to be retrieved from the cache.</param>
/// <returns>The cached items, matching each key of <paramref name="keys"/> respectively. For each missed key,
/// it will contain a <see langword="null" />.</returns>
/// <remarks>
/// <para>As all other <c>Many</c> method, its default implementation just falls back on calling
/// the single operation method in a loop. Cache providers should override it with an actual multiple
/// implementation if they can support it.</para>
/// <para>Additionally, if overriding <c>GetMany</c>, consider overriding also
/// <see cref="PreferMultipleGet"/>.</para>
/// </remarks>
public virtual object[] GetMany(object[] keys)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
var result = new object[keys.Length];
for (var i = 0; i < keys.Length; i++)
{
result[i] = Get(keys[i]);
}
return result;
}
/// <summary>
/// Add multiple items to the cache.
/// </summary>
/// <param name="keys">The keys of the items.</param>
/// <param name="values">The items.</param>
public virtual void PutMany(object[] keys, object[] values)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
if (values == null)
throw new ArgumentNullException(nameof(values));
if (keys.Length != values.Length)
throw new ArgumentException(
$"{nameof(keys)} and {nameof(values)} must have the same length. Found {keys.Length} and " +
$"{values.Length} respectively");
for (var i = 0; i < keys.Length; i++)
{
Put(keys[i], values[i]);
}
}
/// <summary>
/// Lock the items from being concurrently changed.
/// </summary>
/// <param name="keys">The keys of the items.</param>
/// <returns>A lock object to use for unlocking the items. Can be <see langword="null" />.</returns>
/// <remarks>The implementation is allowed to do nothing for non-clustered cache.</remarks>
public virtual object LockMany(object[] keys)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
// When delegating to the single operation lock, agreggate lock values into one.
var lockValues = new object[keys.Length];
for (var i = 0; i < keys.Length; i++)
{
lockValues[i] = Lock(keys[i]);
}
return lockValues;
}
/// <summary>
/// Unlock the items that were previously locked.
/// </summary>
/// <param name="keys">The keys of the items.</param>
/// <param name="lockValue">The lock object to use for unlocking the items, as received from <see cref="Lock"/>.</param>
/// <remarks>The implementation should do nothing if <see cref="Lock"/> own implementation does nothing.</remarks>
public virtual void UnlockMany(object[] keys, object lockValue)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
if (!(lockValue is object[] lockValues) || lockValues.Length != keys.Length)
throw new ArgumentException(
$"When {nameof(UnlockMany)} is not overriden, {nameof(lockValue)} must be an array of the lock " +
$"values resulting of sequentially single-locking each key.",
nameof(lockValue));
for (var i = 0; i < keys.Length; i++)
{
Unlock(keys[i], lockValues[i]);
}
}
#endregion
#region Async basic operations default implementations
/// <summary>
/// Get the item from the cache.
/// </summary>
/// <param name="key">The item key.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
/// <returns>The cached item.</returns>
public virtual Task<object> GetAsync(object key, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object>(cancellationToken);
}
try
{
return Task.FromResult(Get(key));
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}
/// <summary>
/// Put the item into the cache.
/// </summary>
/// <param name="key">The item key.</param>
/// <param name="value">The item.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
public virtual Task PutAsync(object key, object value, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object>(cancellationToken);
}
try
{
Put(key, value);
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}
/// <summary>
/// Remove an item from the cache.
/// </summary>
/// <param name="key">The item key.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
public virtual Task RemoveAsync(object key, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object>(cancellationToken);
}
try
{
Remove(key);
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}
/// <summary>
/// Clear the cache.
/// </summary>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
public virtual Task ClearAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object>(cancellationToken);
}
try
{
Clear();
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}
/// <summary>
/// If this is a clustered cache, lock the item.
/// </summary>
/// <param name="key">The item key.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
/// <returns>A lock object to use for unlocking the key. Can be <see langword="null" />.</returns>
public virtual Task<object> LockAsync(object key, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object>(cancellationToken);
}
try
{
return Task.FromResult(Lock(key));
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}
/// <summary>
/// If this is a clustered cache, unlock the item.
/// </summary>
/// <param name="key">The item key.</param>
/// <param name="lockValue">The lock object to use for unlocking the key, as received from <see cref="Lock"/>.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
public virtual Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object>(cancellationToken);
}
try
{
Unlock(key, lockValue);
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}
#endregion
#region Obsolete ICache implementation
void ICache.Lock(object key)
{
Lock(key);
}
void ICache.Unlock(object key)
{
Unlock(key, null);
}
// Async Generator does not generate an explicit missing async counterpart when a method
// differing only by its return type already exists.
Task ICache.LockAsync(object key, CancellationToken cancellationToken)
{
return LockAsync(key, cancellationToken);
}
#endregion
}
}