/// <summary> /// Stop any other transactions reading or writing this item to/from /// the cache. Send them straight to the database instead. (The lock /// does time out eventually.) This implementation tracks concurrent /// locks by transactions which simultaneously attempt to write to an /// item. /// </summary> public async Task <ISoftLock> LockAsync(CacheKey key, object version, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (await _lockObjectAsync.LockAsync()) { if (log.IsDebugEnabled()) { log.Debug("Invalidating: {0}", key); } try { await(cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); ILockable lockable = (ILockable)await(cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); long timeout = cache.NextTimestamp() + cache.Timeout; CacheLock @lock = lockable == null ? new CacheLock(timeout, NextLockId(), version) : lockable.Lock(timeout, NextLockId()); await(cache.PutAsync(key, @lock, cancellationToken)).ConfigureAwait(false); return(@lock); } finally { await(cache.UnlockAsync(key, cancellationToken)).ConfigureAwait(false); } } }
/// <summary> /// Stop any other transactions reading or writing this item to/from /// the cache. Send them straight to the database instead. (The lock /// does time out eventually.) This implementation tracks concurrent /// locks by transactions which simultaneously attempt to write to an /// item. /// </summary> public async Task <ISoftLock> LockAsync(CacheKey key, object version, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (await(_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false)) { if (log.IsDebugEnabled()) { log.Debug("Invalidating: {0}", key); } var lockValue = await(_cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); try { ILockable lockable = (ILockable)await(Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); long timeout = Cache.NextTimestamp() + Cache.Timeout; CacheLock @lock = lockable == null? CacheLock.Create(timeout, NextLockId(), version) : lockable.Lock(timeout, NextLockId()); await(Cache.PutAsync(key, @lock, cancellationToken)).ConfigureAwait(false); return(@lock); } finally { await(_cache.UnlockAsync(key, lockValue, cancellationToken)).ConfigureAwait(false); } } }
/// <summary> /// Stop any other transactions reading or writing this item to/from /// the cache. Send them straight to the database instead. (The lock /// does time out eventually.) This implementation tracks concurrent /// locks by transactions which simultaneously attempt to write to an /// item. /// </summary> public ISoftLock Lock(CacheKey key, object version) { lock (_lockObject) { if (log.IsDebugEnabled()) { log.Debug("Invalidating: {0}", key); } try { cache.Lock(key); ILockable lockable = (ILockable)cache.Get(key); long timeout = cache.NextTimestamp() + cache.Timeout; CacheLock @lock = lockable == null? CacheLock.Create(timeout, NextLockId(), version) : lockable.Lock(timeout, NextLockId()); cache.Put(key, @lock); return(@lock); } finally { cache.Unlock(key); } } }
internal void HandleLockExpiry(object key) { log.Warn("An item was expired by the cache while it was locked (increase your cache timeout): {0}", key); long ts = cache.NextTimestamp() + cache.Timeout; // create new lock that times out immediately CacheLock @lock = CacheLock.Create(ts, NextLockId(), null); @lock.Unlock(ts); cache.Put(key, @lock); }
/// <summary> /// Re-cache the updated state, if and only if there there are /// no other concurrent soft locks. Release our lock. /// </summary> public async Task <bool> AfterUpdateAsync(CacheKey key, object value, object version, ISoftLock clientLock, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (await _lockObjectAsync.LockAsync()) { if (log.IsDebugEnabled()) { log.Debug("Updating: {0}", key); } try { await(cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); ILockable lockable = (ILockable)await(cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (IsUnlockable(clientLock, lockable)) { CacheLock @lock = (CacheLock)lockable; if (@lock.WasLockedConcurrently) { // just decrement the lock, don't recache // (we don't know which transaction won) await(DecrementLockAsync(key, @lock, cancellationToken)).ConfigureAwait(false); } else { //recache the updated state await(cache.PutAsync(key, new CachedItem(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); if (log.IsDebugEnabled()) { log.Debug("Updated: {0}", key); } } return(true); } else { await(HandleLockExpiryAsync(key, cancellationToken)).ConfigureAwait(false); return(false); } } finally { await(cache.UnlockAsync(key, cancellationToken)).ConfigureAwait(false); } } }
/// <summary> /// decrement a lock and put it back in the cache /// </summary> private Task DecrementLockAsync(object key, CacheLock @lock, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <object>(cancellationToken)); } try { //decrement the lock @lock.Unlock(Cache.NextTimestamp()); return(Cache.PutAsync(key, @lock, cancellationToken)); } catch (System.Exception ex) { return(Task.FromException <object>(ex)); } }
/// <summary> /// Re-cache the updated state, if and only if there there are /// no other concurrent soft locks. Release our lock. /// </summary> public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock clientLock) { lock (_lockObject) { if (log.IsDebugEnabled()) { log.Debug("Updating: {0}", key); } try { cache.Lock(key); ILockable lockable = (ILockable)cache.Get(key); if (IsUnlockable(clientLock, lockable)) { CacheLock @lock = (CacheLock)lockable; if (@lock.WasLockedConcurrently) { // just decrement the lock, don't recache // (we don't know which transaction won) DecrementLock(key, @lock); } else { //recache the updated state cache.Put(key, CachedItem.Create(value, cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Updated: {0}", key); } } return(true); } else { HandleLockExpiry(key); return(false); } } finally { cache.Unlock(key); } } }
internal Task HandleLockExpiryAsync(object key, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <object>(cancellationToken)); } try { log.Warn("An item was expired by the cache while it was locked (increase your cache timeout): {0}", key); long ts = Cache.NextTimestamp() + Cache.Timeout; // create new lock that times out immediately CacheLock @lock = CacheLock.Create(ts, NextLockId(), null); @lock.Unlock(ts); return(Cache.PutAsync(key, @lock, cancellationToken)); } catch (System.Exception ex) { return(Task.FromException <object>(ex)); } }
internal void HandleLockExpiry( object key ) { log.Warn( "An item was expired by the cache while it was locked (increase your cache timeout): " + key ); long ts = cache.NextTimestamp() + cache.Timeout; // create new lock that times out immediately CacheLock @lock = new CacheLock( ts, NextLockId(), null ); @lock.Unlock( ts ); cache.Put( key, @lock ); }
/// <summary> /// decrement a lock and put it back in the cache /// </summary> private void DecrementLock( object key, CacheLock @lock ) { //decrement the lock @lock.Unlock( cache.NextTimestamp() ); cache.Put( key, @lock ); }
/// <summary> /// decrement a lock and put it back in the cache /// </summary> private void DecrementLock(object key, CacheLock @lock) { //decrement the lock @lock.Unlock(cache.NextTimestamp()); cache.Put(key, @lock); }