Skip to content

Commit 8ce0a3a

Browse files
committed
Removed the dictionary of locked keys as it will not be needed with NH 5.2
1 parent e44438d commit 8ce0a3a

File tree

8 files changed

+43
-78
lines changed

8 files changed

+43
-78
lines changed

StackExRedis/NHibernate.Caches.StackExRedis.Tests/Async/Caches/DistributedRedisCache.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,23 @@ public async Task LockAsync(object key, CancellationToken cancellationToken)
6262
{
6363
// A simple locking that requires all instances to obtain the lock
6464
// A real distributed cache should use something like the Redlock algorithm.
65+
var lockValues = new string[_regionStrategies.Length];
6566
try
6667
{
67-
foreach (var strategy in _regionStrategies)
68+
for (var i = 0; i < _regionStrategies.Length; i++)
6869
{
69-
await (strategy.LockAsync(key, cancellationToken));
70+
lockValues[i] = await (_regionStrategies[i].LockAsync(key, cancellationToken));
7071
}
71-
7272
}
7373
catch (CacheException)
7474
{
75-
foreach (var strategy in _regionStrategies)
75+
for (var i = 0; i < _regionStrategies.Length; i++)
7676
{
77-
await (strategy.UnlockAsync(key, cancellationToken));
77+
if (lockValues[i] == null)
78+
{
79+
continue;
80+
}
81+
await (_regionStrategies[i].UnlockAsync(key, lockValues[i], cancellationToken));
7882
}
7983
throw;
8084
}
@@ -85,7 +89,8 @@ public async Task UnlockAsync(object key, CancellationToken cancellationToken)
8589
{
8690
foreach (var strategy in _regionStrategies)
8791
{
88-
await (strategy.UnlockAsync(key, cancellationToken));
92+
// TODO: use the lockValue when upgrading to NH 5.2
93+
await (strategy.UnlockAsync(key, null, cancellationToken));
8994
}
9095
}
9196

StackExRedis/NHibernate.Caches.StackExRedis.Tests/Caches/DistributedRedisCache.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,23 @@ public void Lock(object key)
8383
{
8484
// A simple locking that requires all instances to obtain the lock
8585
// A real distributed cache should use something like the Redlock algorithm.
86+
var lockValues = new string[_regionStrategies.Length];
8687
try
8788
{
88-
foreach (var strategy in _regionStrategies)
89+
for (var i = 0; i < _regionStrategies.Length; i++)
8990
{
90-
strategy.Lock(key);
91+
lockValues[i] = _regionStrategies[i].Lock(key);
9192
}
92-
9393
}
9494
catch (CacheException)
9595
{
96-
foreach (var strategy in _regionStrategies)
96+
for (var i = 0; i < _regionStrategies.Length; i++)
9797
{
98-
strategy.Unlock(key);
98+
if (lockValues[i] == null)
99+
{
100+
continue;
101+
}
102+
_regionStrategies[i].Unlock(key, lockValues[i]);
99103
}
100104
throw;
101105
}
@@ -106,7 +110,8 @@ public void Unlock(object key)
106110
{
107111
foreach (var strategy in _regionStrategies)
108112
{
109-
strategy.Unlock(key);
113+
// TODO: use the lockValue when upgrading to NH 5.2
114+
strategy.Unlock(key, null);
110115
}
111116
}
112117

StackExRedis/NHibernate.Caches.StackExRedis/AbstractRegionStrategy.cs

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,6 @@ public abstract partial class AbstractRegionStrategy
3434

3535
private readonly RedisKeyLocker _keyLocker;
3636

37-
/// <summary>
38-
/// With the current NHibernate version (5.1) the <see cref="ICache.Lock"/> method does not have a
39-
/// return value that would indicate which lock value was used to lock the key, that would then be
40-
/// passed to the <see cref="ICache.Unlock"/> method in order to prevent unlocking keys that were locked
41-
/// by others. Luckily, the <see cref="ICache.Lock"/> and <see cref="ICache.Unlock"/> methods are currently
42-
/// always called inside a lock statement which we abuse by storing the locked key when the
43-
/// <see cref="ICache.Lock"/> is called and use it when <see cref="ICache.Unlock"/> is called. This
44-
/// means that our <see cref="Lock"/> and <see cref="Unlock"/> are not thread safe and have to be called
45-
/// sequentially in order prevent overriding the lock key for a given key.
46-
/// Currently, the <see cref="ICache.Unlock"/> is always called after the <see cref="ICache.Lock"/> method
47-
/// even if an exception occurs which we are also abusing in order to avoid having a special mechanism to
48-
/// clear the dictionary in order to prevent memory leaks.
49-
/// </summary>
50-
private readonly ConcurrentDictionary<string, string> _acquiredKeyLocks = new ConcurrentDictionary<string, string>();
51-
52-
5337
/// <summary>
5438
/// The constructor for creating the region strategy.
5539
/// </summary>
@@ -405,17 +389,7 @@ public virtual string Lock(object key)
405389

406390
var cacheKey = GetCacheKey(key);
407391
Log.Debug("Locking object with key: '{0}'.", cacheKey);
408-
var lockValue = _keyLocker.Lock(cacheKey, LockScript, GetAdditionalKeys(), GetAdditionalValues());
409-
410-
_acquiredKeyLocks.AddOrUpdate(cacheKey, _ => lockValue, (_, currValue) =>
411-
{
412-
Log.Warn(
413-
$"Calling {nameof(Lock)} method for key:'{cacheKey}' that was already locked. " +
414-
$"{nameof(Unlock)} method must be called after each {nameof(Lock)} call for " +
415-
$"the same key prior calling {nameof(Lock)} again with the same key.");
416-
return lockValue;
417-
});
418-
return lockValue;
392+
return _keyLocker.Lock(cacheKey, LockScript, GetAdditionalKeys(), GetAdditionalValues());
419393
}
420394

421395
/// <summary>
@@ -450,8 +424,9 @@ public virtual string LockMany(object[] keys)
450424
/// Unlocks the key.
451425
/// </summary>
452426
/// <param name="key">The key to unlock.</param>
427+
/// <param name="lockValue">The value used to lock the key.</param>
453428
/// <returns>Whether the key was unlocked</returns>
454-
public virtual bool Unlock(object key)
429+
public virtual bool Unlock(object key, string lockValue)
455430
{
456431
if (key == null)
457432
{
@@ -460,14 +435,6 @@ public virtual bool Unlock(object key)
460435

461436
var cacheKey = GetCacheKey(key);
462437
Log.Debug("Unlocking object with key: '{0}'.", cacheKey);
463-
464-
if (!_acquiredKeyLocks.TryRemove(cacheKey, out var lockValue))
465-
{
466-
Log.Warn(
467-
$"Calling {nameof(Unlock)} method for key:'{cacheKey}' that was not locked with {nameof(Lock)} method before.");
468-
return false;
469-
}
470-
471438
var unlocked = _keyLocker.Unlock(cacheKey, lockValue, UnlockScript, GetAdditionalKeys(), GetAdditionalValues());
472439
Log.Debug("Unlock key '{0}' result: {1}", cacheKey, unlocked);
473440
return unlocked;
@@ -477,7 +444,7 @@ public virtual bool Unlock(object key)
477444
/// Unlocks many keys at once.
478445
/// </summary>
479446
/// <param name="keys">The keys to unlock.</param>
480-
/// <param name="lockValue">The value used to lock the keys</param>
447+
/// <param name="lockValue">The value used to lock the keys.</param>
481448
/// <returns>The number of unlocked keys.</returns>
482449
public virtual int UnlockMany(object[] keys, string lockValue)
483450
{

StackExRedis/NHibernate.Caches.StackExRedis/Async/AbstractRegionStrategy.cs

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -321,23 +321,16 @@ public virtual Task<string> LockAsync(object key, CancellationToken cancellation
321321
{
322322
return Task.FromCanceled<string>(cancellationToken);
323323
}
324-
return InternalLockAsync();
325-
async Task<string> InternalLockAsync()
324+
try
326325
{
327326

328327
var cacheKey = GetCacheKey(key);
329328
Log.Debug("Locking object with key: '{0}'.", cacheKey);
330-
var lockValue = await (_keyLocker.LockAsync(cacheKey, LockScript, GetAdditionalKeys(), GetAdditionalValues(), cancellationToken)).ConfigureAwait(false);
331-
332-
_acquiredKeyLocks.AddOrUpdate(cacheKey, _ => lockValue, (_, currValue) =>
333-
{
334-
Log.Warn(
335-
$"Calling {nameof(LockAsync)} method for key:'{cacheKey}' that was already locked. " +
336-
$"{nameof(UnlockAsync)} method must be called after each {nameof(LockAsync)} call for " +
337-
$"the same key prior calling {nameof(LockAsync)} again with the same key.");
338-
return lockValue;
339-
});
340-
return lockValue;
329+
return _keyLocker.LockAsync(cacheKey, LockScript, GetAdditionalKeys(), GetAdditionalValues(), cancellationToken);
330+
}
331+
catch (Exception ex)
332+
{
333+
return Task.FromException<string>(ex);
341334
}
342335
}
343336

@@ -385,9 +378,10 @@ public virtual Task<string> LockManyAsync(object[] keys, CancellationToken cance
385378
/// Unlocks the key.
386379
/// </summary>
387380
/// <param name="key">The key to unlock.</param>
381+
/// <param name="lockValue">The value used to lock the key.</param>
388382
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
389383
/// <returns>Whether the key was unlocked</returns>
390-
public virtual Task<bool> UnlockAsync(object key, CancellationToken cancellationToken)
384+
public virtual Task<bool> UnlockAsync(object key, string lockValue, CancellationToken cancellationToken)
391385
{
392386
if (key == null)
393387
{
@@ -403,14 +397,6 @@ async Task<bool> InternalUnlockAsync()
403397

404398
var cacheKey = GetCacheKey(key);
405399
Log.Debug("Unlocking object with key: '{0}'.", cacheKey);
406-
407-
if (!_acquiredKeyLocks.TryRemove(cacheKey, out var lockValue))
408-
{
409-
Log.Warn(
410-
$"Calling {nameof(UnlockAsync)} method for key:'{cacheKey}' that was not locked with {nameof(LockAsync)} method before.");
411-
return false;
412-
}
413-
414400
var unlocked = await (_keyLocker.UnlockAsync(cacheKey, lockValue, UnlockScript, GetAdditionalKeys(), GetAdditionalValues(), cancellationToken)).ConfigureAwait(false);
415401
Log.Debug("Unlock key '{0}' result: {1}", cacheKey, unlocked);
416402
return unlocked;
@@ -421,7 +407,7 @@ async Task<bool> InternalUnlockAsync()
421407
/// Unlocks many keys at once.
422408
/// </summary>
423409
/// <param name="keys">The keys to unlock.</param>
424-
/// <param name="lockValue">The value used to lock the keys</param>
410+
/// <param name="lockValue">The value used to lock the keys.</param>
425411
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
426412
/// <returns>The number of unlocked keys.</returns>
427413
public virtual Task<int> UnlockManyAsync(object[] keys, string lockValue, CancellationToken cancellationToken)

StackExRedis/NHibernate.Caches.StackExRedis/Async/DefaultRegionStrategy.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,12 @@ public override async Task<long> RemoveManyAsync(object[] keys, CancellationToke
182182
}
183183

184184
/// <inheritdoc />
185-
public override async Task<bool> UnlockAsync(object key, CancellationToken cancellationToken)
185+
public override async Task<bool> UnlockAsync(object key, string lockValue, CancellationToken cancellationToken)
186186
{
187187
cancellationToken.ThrowIfCancellationRequested();
188188
try
189189
{
190-
return await (base.UnlockAsync(key, cancellationToken)).ConfigureAwait(false);
190+
return await (base.UnlockAsync(key, lockValue, cancellationToken)).ConfigureAwait(false);
191191
}
192192
catch (RedisServerException e) when (e.Message == InvalidVersionMessage)
193193
{

StackExRedis/NHibernate.Caches.StackExRedis/Async/RedisCache.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ public Task UnlockAsync(object key, CancellationToken cancellationToken)
115115
{
116116
return Task.FromCanceled<object>(cancellationToken);
117117
}
118-
return RegionStrategy.UnlockAsync(key, cancellationToken);
118+
// TODO: use the lockValue when upgrading to NH 5.2
119+
return RegionStrategy.UnlockAsync(key, null, cancellationToken);
119120
}
120121

121122
/// <inheritdoc />

StackExRedis/NHibernate.Caches.StackExRedis/DefaultRegionStrategy.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,11 @@ public override long RemoveMany(object[] keys)
259259
}
260260

261261
/// <inheritdoc />
262-
public override bool Unlock(object key)
262+
public override bool Unlock(object key, string lockValue)
263263
{
264264
try
265265
{
266-
return base.Unlock(key);
266+
return base.Unlock(key, lockValue);
267267
}
268268
catch (RedisServerException e) when (e.Message == InvalidVersionMessage)
269269
{

StackExRedis/NHibernate.Caches.StackExRedis/RedisCache.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ public object LockMany(object[] keys)
9999
/// <inheritdoc />
100100
public void Unlock(object key)
101101
{
102-
RegionStrategy.Unlock(key);
102+
// TODO: use the lockValue when upgrading to NH 5.2
103+
RegionStrategy.Unlock(key, null);
103104
}
104105

105106
/// <inheritdoc />

0 commit comments

Comments
 (0)