/// <inheritdoc /> public override string Lock(object key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } var lockKey = GetLockKey(key); Log.Debug("Locking key: '{0}'.", lockKey); var lockValue = _lockValueProvider.GetValue(); using (var cacheLockValue = new CacheLockValue(lockValue)) { object Context() => new KeyValuePair <string, CacheLockValue>(lockKey, cacheLockValue); return(_retryPolicy .Execute(() => { if (!LockLocal(lockKey, cacheLockValue)) { Log.Debug("Failed to acquire lock for key '{0}' in the local cache, retrying...", lockKey); return null; // Retry } cacheLockValue.Setup(); var subscriberCount = _subscriber.Publish(_synchronizationChannel, Serializer.Serialize(new CacheSynchronizationMessage { OperationType = OperationType.Lock, Timestamp = DateTime.UtcNow.Ticks, ClientId = _clientId, Data = new LockData { LockKey = lockKey, LockValue = lockValue } })) - 1; if (subscriberCount == 0) { Log.Debug("Acquired lock for key '{0}', no other caches were involved.", lockKey); // We are the only one subscribed return cacheLockValue.Value; } Log.Debug("Waiting lock result from '{0}' other local caches.", subscriberCount); IncreaseLock(cacheLockValue, subscriberCount); if (!cacheLockValue.Semaphore.Wait(_lockAcquireTimeout) || cacheLockValue.Failed) { Log.Debug("Failed to acquire lock for key '{0}' from '{1}' other local caches, retrying...", lockKey, subscriberCount); return null; } Log.Debug("Acquired lock for key '{0}', '{1}' other caches were involved.", lockKey, subscriberCount); return cacheLockValue.Value; }, Context)); } }
private bool LockManyLocal(string[] lockKeys, CacheLockValue lockValue) { foreach (var lockKey in lockKeys) { if (!LockLocal(lockKey, lockValue)) { return(false); } } return(true); }
private bool LockLocal(string lockKey, CacheLockValue lockValue) { lock (_lockLock) { var value = (CacheLockValue)_memoryCache.Get(lockKey); if (value != null && !value.Equals(lockValue)) { return(false); } _memoryCache.Put(lockKey, lockValue, _lockKeyTimeout); return(true); } }
private void IncreaseLock(CacheLockValue cacheLockValue, long value) { lock (cacheLockValue) { if (cacheLockValue.Failed) { return; } cacheLockValue.RemainingLocks += value; Log.Debug("Remaining locks to acquire: '{0}'", cacheLockValue.RemainingLocks); if (cacheLockValue.RemainingLocks == 0) { cacheLockValue.Semaphore.Release(); } } }
/// <inheritdoc /> public override Task <string> LockManyAsync(object[] keys, CancellationToken cancellationToken) { if (keys == null) { throw new ArgumentNullException(nameof(keys)); } if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <string>(cancellationToken)); } return(InternalLockManyAsync()); async Task <string> InternalLockManyAsync() { var lockKeys = new string[keys.Length]; var lockValue = _lockValueProvider.GetValue(); for (var i = 0; i < keys.Length; i++) { lockKeys[i] = GetLockKey(keys[i]); Log.Debug("Locking key: '{0}'.", lockKeys[i]); } using (var cacheLockValue = new CacheLockValue(lockValue)) { object Context() => new KeyValuePair <string[], CacheLockValue>(lockKeys, cacheLockValue); return(await(_retryPolicy .ExecuteAsync(async() => { if (!LockManyLocal(lockKeys, cacheLockValue)) { Log.Debug("Failed to acquire lock for '{0}' keys in the local cache, retrying...", lockKeys.Length); return null; // Retry } cacheLockValue.Setup(); var subscriberCount = await(_subscriber.PublishAsync(_synchronizationChannel, Serializer.Serialize(new CacheSynchronizationMessage { OperationType = OperationType.LockMany, Timestamp = DateTime.UtcNow.Ticks, ClientId = _clientId, Data = new LockManyData { LockKeys = lockKeys, LockValue = lockValue } }))).ConfigureAwait(false) - 1; if (subscriberCount == 0) { Log.Debug("Acquired lock for '{0}' keys, no other caches were involved.", lockKeys.Length); // We are the only one subscribed return cacheLockValue.Value; } Log.Debug("Waiting lock result from '{0}' other local caches.", subscriberCount); IncreaseLock(cacheLockValue, subscriberCount); if (!await(cacheLockValue.Semaphore.WaitAsync(_lockAcquireTimeout, cancellationToken)).ConfigureAwait(false) || cacheLockValue.Failed) { Log.Debug("Failed to acquire lock for '{0}' keys from '{1}' other local caches, retrying...", lockKeys.Length, subscriberCount); return null; } Log.Debug("Acquired lock for '{0}' keys, '{1}' other caches were involved.", lockKeys.Length, subscriberCount); return cacheLockValue.Value; }, Context, cancellationToken)).ConfigureAwait(false)); } } }