public async Task ReleaseAsync(CacheKey key, ISoftLock clientLock, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (await _lockObjectAsync.LockAsync()) { if (log.IsDebugEnabled) { log.Debug("Releasing: " + key); } try { await(cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); ILockable lockable = (ILockable)await(cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (IsUnlockable(clientLock, lockable)) { await(DecrementLockAsync(key, (CacheLock)lockable, cancellationToken)).ConfigureAwait(false); } else { await(HandleLockExpiryAsync(key, cancellationToken)).ConfigureAwait(false); } } finally { await(cache.UnlockAsync(key, cancellationToken)).ConfigureAwait(false); } } }
public async Task ReleaseAsync(CacheKey key, ISoftLock clientLock, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (await(_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false)) { if (log.IsDebugEnabled()) { log.Debug("Releasing: {0}", key); } var lockValue = await(_cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); try { ILockable lockable = (ILockable)await(Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (IsUnlockable(clientLock, lockable)) { await(DecrementLockAsync(key, (CacheLock)lockable, cancellationToken)).ConfigureAwait(false); } else { await(HandleLockExpiryAsync(key, cancellationToken)).ConfigureAwait(false); } } finally { await(_cache.UnlockAsync(key, lockValue, cancellationToken)).ConfigureAwait(false); } } }
/// <summary> /// If the class to be loaded has been configured with a cache, then lock /// given id in that cache and then perform the load. /// </summary> /// <returns> The loaded entity </returns> protected virtual object LockAndLoad(LoadEvent @event, IEntityPersister persister, EntityKey keyToLoad, LoadType options, ISessionImplementor source) { ISoftLock sLock = null; CacheKey ck; if (persister.HasCache) { ck = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); sLock = persister.Cache.Lock(ck, null); } else { ck = null; } object entity; try { entity = Load(@event, persister, keyToLoad, options); } finally { if (persister.HasCache) { persister.Cache.Release(ck, sLock); } } object proxy = @event.Session.PersistenceContext.ProxyFor(persister, keyToLoad, entity); return(proxy); }
public void Release(CacheKey key, ISoftLock clientLock) { lock (_lockObject) { if (log.IsDebugEnabled()) { log.Debug("Releasing: {0}", key); } try { cache.Lock(key); ILockable lockable = (ILockable)cache.Get(key); if (IsUnlockable(clientLock, lockable)) { DecrementLock(key, (CacheLock)lockable); } else { HandleLockExpiry(key); } } finally { cache.Unlock(key); } } }
/// <summary> /// If the class to be loaded has been configured with a cache, then lock /// given id in that cache and then perform the load. /// </summary> /// <returns> The loaded entity </returns> protected virtual async Task <object> LockAndLoadAsync(LoadEvent @event, IEntityPersister persister, EntityKey keyToLoad, LoadType options, ISessionImplementor source, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ISoftLock sLock = null; CacheKey ck; if (persister.HasCache) { ck = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); sLock = await(persister.Cache.LockAsync(ck, null, cancellationToken)).ConfigureAwait(false); } else { ck = null; } object entity; try { entity = await(LoadAsync(@event, persister, keyToLoad, options, cancellationToken)).ConfigureAwait(false); } finally { if (persister.HasCache) { await(persister.Cache.ReleaseAsync(ck, sLock, cancellationToken)).ConfigureAwait(false); } } object proxy = @event.Session.PersistenceContext.ProxyFor(persister, keyToLoad, entity); return(proxy); }
/// <summary></summary> public override void Execute() { CacheKey ck = null; if (Persister.HasCache) { ck = new CacheKey( Id, Persister.IdentifierType, (string) Persister.IdentifierSpace, Session.Factory ); _lock = Persister.Cache.Lock(ck, lastVersion); } Persister.Update(Id, state, dirtyFields, hasDirtyCollection, previousState, lastVersion, Instance, Session); EntityEntry entry = Session.GetEntry(Instance); if (entry == null) { throw new AssertionFailure("possible nonthreadsafe access to session"); } if (entry.Status == Status.Loaded || Persister.IsVersionPropertyGenerated) { // get the updated snapshot of the entity state by cloning current state; // it is safe to copy in place, since by this time no-one else (should have) // has a reference to the array TypeFactory.DeepCopy( state, Persister.PropertyTypes, Persister.PropertyCheckability, state); if (Persister.HasUpdateGeneratedProperties) { // this entity defines proeprty generation, so process those generated // values... Persister.ProcessUpdateGeneratedProperties(Id, Instance, state, Session); if (Persister.IsVersionPropertyGenerated) { nextVersion = Versioning.GetVersion(state, Persister); } } // have the entity entry perform post-update processing, passing it the // update state and the new version (if one). entry.PostUpdate(Instance, state, nextVersion); } if (Persister.HasCache) { if (Persister.IsCacheInvalidationRequired || entry.Status != Status.Loaded) { Persister.Cache.Evict(ck); } else { // TODO: Inefficient if that cache is just going to ignore the updated state! cacheEntry = new CacheEntry(Instance, Persister, Session); Persister.Cache.Update(ck, cacheEntry); } } }
/// <summary> /// Is the client's lock commensurate with the item in the cache? /// If it is not, we know that the cache expired the original /// lock. /// </summary> private bool IsUnlockable(ISoftLock clientLock, ILockable myLock) { //null clientLock is remotely possible but will never happen in practice return(myLock != null && myLock.IsLock && clientLock != null && ((CacheLock)clientLock).Id == ((CacheLock)myLock).Id); }
/// <summary></summary> public override void Execute() { if( Persister.HasCache ) { lck = Persister.Cache.Lock( Id, version ); } Persister.Delete( Id, version, Instance, Session ); Session.PostDelete( Instance ); }
/// <summary></summary> public override void Execute() { if (Persister.HasCache) { lck = Persister.Cache.Lock(Id, version); } Persister.Delete(Id, version, Instance, Session); Session.PostDelete(Instance); }
/// <summary> /// Invalidate the item (again, for safety). /// </summary> public void Release(CacheKey key, ISoftLock @lock) { if (log.IsDebugEnabled) { log.Debug("Invalidating (again): " + key); } cache.Remove(key); }
/// <summary> /// /// </summary> public void BeforeExecutions( ) { // we need to obtain the lock before any actions are // executed, since this may be an inverse="true" // bidirectional association and it is one of the // earlier entity actions which actually updates // the database (this action is resposible for // second-level cache invalidation only) if (persister.HasCache) { lck = persister.Cache.Lock(id, null); //collections don't have version numbers :-( } }
/// <summary> Called before executing any actions</summary> public virtual void BeforeExecutions() { // we need to obtain the lock before any actions are // executed, since this may be an inverse="true" // bidirectional association and it is one of the // earlier entity actions which actually updates // the database (this action is responsible for // second-level cache invalidation only) if (persister.HasCache) { CacheKey ck = session.GenerateCacheKey(key, persister.KeyType, persister.Role); softLock = persister.Cache.Lock(ck, null); } }
/// <summary></summary> public override void Execute() { if (Persister.HasCache) { CacheKey ck = new CacheKey( Id, Persister.IdentifierType, (string) Persister.IdentifierSpace, Session.Factory ); lck = Persister.Cache.Lock(ck, version); } Persister.Delete(Id, version, Instance, Session); Session.PostDelete(Instance); }
/// <summary> /// Unsupported! /// </summary> public Task ReleaseAsync(CacheKey key, ISoftLock @lock, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <object>(cancellationToken)); } try { Release(key, @lock); return(Task.CompletedTask); } catch (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); } } }
/// <summary> /// Invalidate the item (again, for safety). /// </summary> public Task ReleaseAsync(CacheKey key, ISoftLock @lock, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <object>(cancellationToken)); } try { if (log.IsDebugEnabled()) { log.Debug("Invalidating (again): {0}", key); } return(cache.RemoveAsync(key, cancellationToken)); } catch (Exception ex) { return(Task.FromException <object>(ex)); } }
/// <summary></summary> public override void Execute() { if( Persister.HasCache ) { _lock = Persister.Cache.Lock( Id, lastVersion ); } Persister.Update( Id, fields, dirtyFields, oldFields, lastVersion, Instance, Session ); Session.PostUpdate( Instance, updatedState, nextVersion ); if ( Persister.HasCache ) { if ( Persister.IsCacheInvalidationRequired ) { Persister.Cache.Evict( Id ); } else { // TODO: Inefficient if that cache is just going to ignore the updated state! cacheEntry = new CacheEntry( Instance, Persister, Session ); Persister.Cache.Update( Id, cacheEntry ); } } }
/// <summary></summary> public override void Execute() { if (Persister.HasCache) { _lock = Persister.Cache.Lock(Id, lastVersion); } Persister.Update(Id, fields, dirtyFields, oldFields, lastVersion, Instance, Session); Session.PostUpdate(Instance, updatedState, nextVersion); if (Persister.HasCache) { if (Persister.IsCacheInvalidationRequired) { Persister.Cache.Evict(Id); } else { // TODO: Inefficient if that cache is just going to ignore the updated state! cacheEntry = new CacheEntry(Instance, Persister, Session); Persister.Cache.Update(Id, cacheEntry); } } }
/// <summary> /// Invalidate the item (again, for safety). /// </summary> public async Task <bool> AfterUpdateAsync(CacheKey key, object value, object version, ISoftLock @lock, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); await(ReleaseAsync(key, @lock, cancellationToken)).ConfigureAwait(false); return(false); }
/// <summary> /// /// </summary> /// <param name="key"></param> /// <param name="clientLock"></param> public void Release( object key, ISoftLock clientLock ) { lock( _lockObject ) { if( log.IsDebugEnabled ) { log.Debug( "Releasing: " + key ); } try { cache.Lock( key ); ILockable lockable = ( ILockable ) cache.Get( key ); if( IsUnlockable( clientLock, lockable ) ) { DecrementLock( key, ( CacheLock ) lockable ); } else { HandleLockExpiry( key ); } } finally { cache.Unlock( key ); } } }
/// <summary> /// Invalidate the item (again, for safety). /// </summary> public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock @lock) { Release(key, @lock); return(false); }
/// <summary> /// Invalidate the item (again, for safety). /// </summary> public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock @lock) { Release(key, @lock); return false; }
public override void Execute() { ISessionImplementor session = Session; object id = Id; IEntityPersister persister = Persister; object instance = Instance; bool statsEnabled = Session.Factory.Statistics.IsStatisticsEnabled; Stopwatch stopwatch = null; if (statsEnabled) { stopwatch = Stopwatch.StartNew(); } bool veto = PreUpdate(); ISessionFactoryImplementor factory = Session.Factory; if (persister.IsVersionPropertyGenerated) { // we need to grab the version value from the entity, otherwise // we have issues with generated-version entities that may have // multiple actions queued during the same flush previousVersion = persister.GetVersion(instance); } CacheKey ck = null; if (persister.HasCache) { ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); slock = persister.Cache.Lock(ck, previousVersion); } if (!veto) { persister.Update(id, state, dirtyFields, hasDirtyCollection, previousState, previousVersion, instance, null, session); } EntityEntry entry = Session.PersistenceContext.GetEntry(instance); if (entry == null) { throw new AssertionFailure("Possible nonthreadsafe access to session"); } if (entry.Status == Status.Loaded || persister.IsVersionPropertyGenerated) { // get the updated snapshot of the entity state by cloning current state; // it is safe to copy in place, since by this time no-one else (should have) // has a reference to the array TypeHelper.DeepCopy(state, persister.PropertyTypes, persister.PropertyCheckability, state, Session); if (persister.HasUpdateGeneratedProperties) { // this entity defines property generation, so process those generated // values... persister.ProcessUpdateGeneratedProperties(id, instance, state, Session); if (persister.IsVersionPropertyGenerated) { nextVersion = Versioning.GetVersion(state, persister); } } // have the entity entry perform post-update processing, passing it the // update state and the new version (if one). entry.PostUpdate(instance, state, nextVersion); } if (persister.HasCache) { if (persister.IsCacheInvalidationRequired || entry.Status != Status.Loaded) { persister.Cache.Evict(ck); } else { CacheEntry ce = CacheEntry.Create(state, persister, nextVersion, Session, instance); cacheEntry = persister.CacheEntryStructure.Structure(ce); bool put = persister.Cache.Update(ck, cacheEntry, nextVersion, previousVersion); if (put && factory.Statistics.IsStatisticsEnabled) { factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); } } } PostUpdate(); if (statsEnabled && !veto) { stopwatch.Stop(); factory.StatisticsImplementor.UpdateEntity(Persister.EntityName, stopwatch.Elapsed); } }
/// <summary> /// Unsupported! /// </summary> public void AfterUpdate(CacheKey key, object value, object version, ISoftLock @lock) { log.Error("Application attempted to edit read only item: " + key); throw new InvalidOperationException("Can't write to a readonly object"); }
/// <summary> /// Unsupported! /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="version"></param> /// <param name="lock"></param> public void AfterUpdate(object key, object value, object version, ISoftLock @lock) { log.Error("Application attempted to edit read only item: " + key); throw new InvalidOperationException("Can't write to a readonly object"); }
public override void Execute() { ISessionImplementor session = Session; object id = Id; IEntityPersister persister = Persister; object instance = Instance; bool statsEnabled = Session.Factory.Statistics.IsStatisticsEnabled; Stopwatch stopwatch = null; if (statsEnabled) { stopwatch = Stopwatch.StartNew(); } bool veto = PreUpdate(); ISessionFactoryImplementor factory = Session.Factory; if (persister.IsVersionPropertyGenerated) { // we need to grab the version value from the entity, otherwise // we have issues with generated-version entities that may have // multiple actions queued during the same flush previousVersion = persister.GetVersion(instance, session.EntityMode); } CacheKey ck = null; if (persister.HasCache) { ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); slock = persister.Cache.Lock(ck, previousVersion); } if (!veto) { persister.Update(id, state, dirtyFields, hasDirtyCollection, previousState, previousVersion, instance, null, session); } EntityEntry entry = Session.PersistenceContext.GetEntry(instance); if (entry == null) { throw new AssertionFailure("Possible nonthreadsafe access to session"); } if (entry.Status == Status.Loaded || persister.IsVersionPropertyGenerated) { // get the updated snapshot of the entity state by cloning current state; // it is safe to copy in place, since by this time no-one else (should have) // has a reference to the array TypeHelper.DeepCopy(state, persister.PropertyTypes, persister.PropertyCheckability, state, Session); if (persister.HasUpdateGeneratedProperties) { // this entity defines property generation, so process those generated // values... persister.ProcessUpdateGeneratedProperties(id, instance, state, Session); if (persister.IsVersionPropertyGenerated) { nextVersion = Versioning.GetVersion(state, persister); } } // have the entity entry perform post-update processing, passing it the // update state and the new version (if one). entry.PostUpdate(instance, state, nextVersion); } if (persister.HasCache) { if (persister.IsCacheInvalidationRequired || entry.Status != Status.Loaded) { persister.Cache.Evict(ck); } else { CacheEntry ce = new CacheEntry(state, persister, persister.HasUninitializedLazyProperties(instance, session.EntityMode), nextVersion, Session, instance); cacheEntry = persister.CacheEntryStructure.Structure(ce); bool put = persister.Cache.Update(ck, cacheEntry, nextVersion, previousVersion); if (put && factory.Statistics.IsStatisticsEnabled) { factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); } } } PostUpdate(); if (statsEnabled && !veto) { stopwatch.Stop(); factory.StatisticsImplementor.UpdateEntity(Persister.EntityName, stopwatch.Elapsed); } }
/// <summary> /// Unsupported! /// </summary> /// <param name="key"></param> /// <param name="lock"></param> public void Release(object key, ISoftLock @lock) { log.Error("Application attempted to edit read only item: " + key); //throw new InvalidOperationException( "Can't write to a readonly object" ); }
/// <summary> /// Is the client's lock commensurate with the item in the cache? /// If it is not, we know that the cache expired the original /// lock. /// </summary> private bool IsUnlockable( ISoftLock clientLock, ILockable myLock ) { //null clientLock is remotely possible but will never happen in practice return myLock != null && myLock.IsLock && clientLock != null && ( ( CacheLock ) clientLock ).Id == ( ( CacheLock ) myLock ).Id; }
/// <summary> /// Re-cache the updated state, if and only if there there are /// no other concurrent soft locks. Release our lock. /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="version"></param> /// <param name="clientLock"></param> public void AfterUpdate( object key, object value, object version, ISoftLock clientLock ) { lock( _lockObject ) { if( log.IsDebugEnabled ) { log.Debug( "Updating: " + 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, new CachedItem( value, cache.NextTimestamp(), version ) ); if( log.IsDebugEnabled ) { log.Debug( "Updated: " + key ); } } } else { HandleLockExpiry( key ); } } finally { cache.Unlock( key ); } } }
/// <summary> /// Unsupported! /// </summary> public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock @lock) { log.Error("Application attempted to edit read only item: " + key); throw new InvalidOperationException("ReadOnlyCache: Can't write to a readonly object " + key.EntityOrRoleName); }
/// <summary> /// Unsupported! /// </summary> public void Release(CacheKey key, ISoftLock @lock) { log.Error("Application attempted to edit read only item: " + key); }
/// <summary> /// /// </summary> public void BeforeExecutions() { // we need to obtain the lock before any actions are // executed, since this may be an inverse="true" // bidirectional association and it is one of the // earlier entity actions which actually updates // the database (this action is resposible for // second-level cache invalidation only) if (persister.HasCache) { CacheKey ck = new CacheKey( id, persister.KeyType, persister.Role, session.Factory ); lck = persister.Cache.Lock(ck, null); //collections don't have version numbers :-( } }
/// <summary> /// Invalidate the item (again, for safety). /// </summary> public void Release(CacheKey key, ISoftLock @lock) { if (log.IsDebugEnabled) { log.Debug("Invalidating (again): " + key); } cache.Remove(key); }
public override void Execute() { object id = Id; IEntityPersister persister = Persister; ISessionImplementor session = Session; object instance = Instance; bool statsEnabled = Session.Factory.Statistics.IsStatisticsEnabled; Stopwatch stopwatch = null; if (statsEnabled) { stopwatch = Stopwatch.StartNew(); } bool veto = PreDelete(); object tmpVersion = version; if (persister.IsVersionPropertyGenerated) { // we need to grab the version value from the entity, otherwise // we have issues with generated-version entities that may have // multiple actions queued during the same flush tmpVersion = persister.GetVersion(instance, session.EntityMode); } CacheKey ck; if (persister.HasCache) { ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); sLock = persister.Cache.Lock(ck, version); } else { ck = null; } if (!isCascadeDeleteEnabled && !veto) { persister.Delete(id, tmpVersion, instance, session); } //postDelete: // After actually deleting a row, record the fact that the instance no longer // exists on the database (needed for identity-column key generation), and // remove it from the session cache IPersistenceContext persistenceContext = session.PersistenceContext; EntityEntry entry = persistenceContext.RemoveEntry(instance); if (entry == null) { throw new AssertionFailure("Possible nonthreadsafe access to session"); } entry.PostDelete(); EntityKey key = session.GenerateEntityKey(entry.Id, entry.Persister); persistenceContext.RemoveEntity(key); persistenceContext.RemoveProxy(key); if (persister.HasCache) persister.Cache.Evict(ck); PostDelete(); if (statsEnabled && !veto) { stopwatch.Stop(); Session.Factory.StatisticsImplementor.DeleteEntity(Persister.EntityName, stopwatch.Elapsed); } }
public void DoTestCache(ICacheProvider cacheProvider) { ICache cache = cacheProvider.BuildCache(typeof(String).FullName, new Dictionary <string, string>()); long longBefore = Timestamper.Next(); Thread.Sleep(15); long before = Timestamper.Next(); Thread.Sleep(15); ICacheConcurrencyStrategy ccs = new ReadWriteCache(); ccs.Cache = cache; // cache something CacheKey fooKey = CreateCacheKey("foo"); Assert.IsTrue(ccs.Put(fooKey, "foo", before, null, null, false)); Thread.Sleep(15); long after = Timestamper.Next(); Assert.IsNull(ccs.Get(fooKey, longBefore)); Assert.AreEqual("foo", ccs.Get(fooKey, after)); Assert.IsFalse(ccs.Put(fooKey, "foo", before, null, null, false)); // update it; ISoftLock fooLock = ccs.Lock(fooKey, null); Assert.IsNull(ccs.Get(fooKey, after)); Assert.IsNull(ccs.Get(fooKey, longBefore)); Assert.IsFalse(ccs.Put(fooKey, "foo", before, null, null, false)); Thread.Sleep(15); long whileLocked = Timestamper.Next(); Assert.IsFalse(ccs.Put(fooKey, "foo", whileLocked, null, null, false)); Thread.Sleep(15); ccs.Release(fooKey, fooLock); Assert.IsNull(ccs.Get(fooKey, after)); Assert.IsNull(ccs.Get(fooKey, longBefore)); Assert.IsFalse(ccs.Put(fooKey, "bar", whileLocked, null, null, false)); Assert.IsFalse(ccs.Put(fooKey, "bar", after, null, null, false)); Thread.Sleep(15); long longAfter = Timestamper.Next(); Assert.IsTrue(ccs.Put(fooKey, "baz", longAfter, null, null, false)); Assert.IsNull(ccs.Get(fooKey, after)); Assert.IsNull(ccs.Get(fooKey, whileLocked)); Thread.Sleep(15); long longLongAfter = Timestamper.Next(); Assert.AreEqual("baz", ccs.Get(fooKey, longLongAfter)); // update it again, with multiple locks ISoftLock fooLock1 = ccs.Lock(fooKey, null); ISoftLock fooLock2 = ccs.Lock(fooKey, null); Assert.IsNull(ccs.Get(fooKey, longLongAfter)); Thread.Sleep(15); whileLocked = Timestamper.Next(); Assert.IsFalse(ccs.Put(fooKey, "foo", whileLocked, null, null, false)); Thread.Sleep(15); ccs.Release(fooKey, fooLock2); Thread.Sleep(15); long betweenReleases = Timestamper.Next(); Assert.IsFalse(ccs.Put(fooKey, "bar", betweenReleases, null, null, false)); Assert.IsNull(ccs.Get(fooKey, betweenReleases)); Thread.Sleep(15); ccs.Release(fooKey, fooLock1); Assert.IsFalse(ccs.Put(fooKey, "bar", whileLocked, null, null, false)); Thread.Sleep(15); longAfter = Timestamper.Next(); Assert.IsTrue(ccs.Put(fooKey, "baz", longAfter, null, null, false)); Assert.IsNull(ccs.Get(fooKey, whileLocked)); Thread.Sleep(15); longLongAfter = Timestamper.Next(); Assert.AreEqual("baz", ccs.Get(fooKey, longLongAfter)); }
public async Task DoTestCacheAsync(ICacheProvider cacheProvider, CancellationToken cancellationToken = default(CancellationToken)) { ICache cache = cacheProvider.BuildCache(typeof(String).FullName, new Dictionary <string, string>()); long longBefore = Timestamper.Next(); await(Task.Delay(15, cancellationToken)); long before = Timestamper.Next(); await(Task.Delay(15, cancellationToken)); ICacheConcurrencyStrategy ccs = new ReadWriteCache(); ccs.Cache = cache; // cache something CacheKey fooKey = CreateCacheKey("foo"); Assert.IsTrue(await(ccs.PutAsync(fooKey, "foo", before, null, null, false, cancellationToken))); await(Task.Delay(15, cancellationToken)); long after = Timestamper.Next(); Assert.IsNull(await(ccs.GetAsync(fooKey, longBefore, cancellationToken))); Assert.AreEqual("foo", await(ccs.GetAsync(fooKey, after, cancellationToken))); Assert.IsFalse(await(ccs.PutAsync(fooKey, "foo", before, null, null, false, cancellationToken))); // update it; ISoftLock fooLock = await(ccs.LockAsync(fooKey, null, cancellationToken)); Assert.IsNull(await(ccs.GetAsync(fooKey, after, cancellationToken))); Assert.IsNull(await(ccs.GetAsync(fooKey, longBefore, cancellationToken))); Assert.IsFalse(await(ccs.PutAsync(fooKey, "foo", before, null, null, false, cancellationToken))); await(Task.Delay(15, cancellationToken)); long whileLocked = Timestamper.Next(); Assert.IsFalse(await(ccs.PutAsync(fooKey, "foo", whileLocked, null, null, false, cancellationToken))); await(Task.Delay(15, cancellationToken)); await(ccs.ReleaseAsync(fooKey, fooLock, cancellationToken)); Assert.IsNull(await(ccs.GetAsync(fooKey, after, cancellationToken))); Assert.IsNull(await(ccs.GetAsync(fooKey, longBefore, cancellationToken))); Assert.IsFalse(await(ccs.PutAsync(fooKey, "bar", whileLocked, null, null, false, cancellationToken))); Assert.IsFalse(await(ccs.PutAsync(fooKey, "bar", after, null, null, false, cancellationToken))); await(Task.Delay(15, cancellationToken)); long longAfter = Timestamper.Next(); Assert.IsTrue(await(ccs.PutAsync(fooKey, "baz", longAfter, null, null, false, cancellationToken))); Assert.IsNull(await(ccs.GetAsync(fooKey, after, cancellationToken))); Assert.IsNull(await(ccs.GetAsync(fooKey, whileLocked, cancellationToken))); await(Task.Delay(15, cancellationToken)); long longLongAfter = Timestamper.Next(); Assert.AreEqual("baz", await(ccs.GetAsync(fooKey, longLongAfter, cancellationToken))); // update it again, with multiple locks ISoftLock fooLock1 = await(ccs.LockAsync(fooKey, null, cancellationToken)); ISoftLock fooLock2 = await(ccs.LockAsync(fooKey, null, cancellationToken)); Assert.IsNull(await(ccs.GetAsync(fooKey, longLongAfter, cancellationToken))); await(Task.Delay(15, cancellationToken)); whileLocked = Timestamper.Next(); Assert.IsFalse(await(ccs.PutAsync(fooKey, "foo", whileLocked, null, null, false, cancellationToken))); await(Task.Delay(15, cancellationToken)); await(ccs.ReleaseAsync(fooKey, fooLock2, cancellationToken)); await(Task.Delay(15, cancellationToken)); long betweenReleases = Timestamper.Next(); Assert.IsFalse(await(ccs.PutAsync(fooKey, "bar", betweenReleases, null, null, false, cancellationToken))); Assert.IsNull(await(ccs.GetAsync(fooKey, betweenReleases, cancellationToken))); await(Task.Delay(15, cancellationToken)); await(ccs.ReleaseAsync(fooKey, fooLock1, cancellationToken)); Assert.IsFalse(await(ccs.PutAsync(fooKey, "bar", whileLocked, null, null, false, cancellationToken))); await(Task.Delay(15, cancellationToken)); longAfter = Timestamper.Next(); Assert.IsTrue(await(ccs.PutAsync(fooKey, "baz", longAfter, null, null, false, cancellationToken))); Assert.IsNull(await(ccs.GetAsync(fooKey, whileLocked, cancellationToken))); await(Task.Delay(15, cancellationToken)); longLongAfter = Timestamper.Next(); Assert.AreEqual("baz", await(ccs.GetAsync(fooKey, longLongAfter, cancellationToken))); }
/// <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(_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false)) { if (log.IsDebugEnabled()) { log.Debug("Updating: {0}", key); } var lockValue = await(_cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); try { 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, CachedItem.Create(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, lockValue, cancellationToken)).ConfigureAwait(false); } } }
/// <summary> /// Invalidate the item (again, for safety). /// </summary> public void AfterUpdate(CacheKey key, object value, object version, ISoftLock @lock) { Release(key, @lock); }
/// <summary> /// Unsupported! /// </summary> public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock @lock) { log.Error("Application attempted to edit read only item: " + key); throw new InvalidOperationException("ReadOnlyCache: Can't write to a readonly object " + key.EntityOrRoleName); }
/// <summary> /// Unsupported! /// </summary> public void Release(CacheKey key, ISoftLock @lock) { log.Error("Application attempted to edit read only item: " + key); //throw new InvalidOperationException( "Can't write to a readonly object" ); }
public override void Execute() { object id = Id; IEntityPersister persister = Persister; ISessionImplementor session = Session; object instance = Instance; bool statsEnabled = Session.Factory.Statistics.IsStatisticsEnabled; Stopwatch stopwatch = null; if (statsEnabled) { stopwatch = Stopwatch.StartNew(); } bool veto = PreDelete(); object tmpVersion = version; if (persister.IsVersionPropertyGenerated) { // we need to grab the version value from the entity, otherwise // we have issues with generated-version entities that may have // multiple actions queued during the same flush tmpVersion = persister.GetVersion(instance, session.EntityMode); } CacheKey ck; if (persister.HasCache) { ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, session.EntityMode, session.Factory); sLock = persister.Cache.Lock(ck, version); } else { ck = null; } if (!isCascadeDeleteEnabled && !veto) { persister.Delete(id, tmpVersion, instance, session); } //postDelete: // After actually deleting a row, record the fact that the instance no longer // exists on the database (needed for identity-column key generation), and // remove it from the session cache IPersistenceContext persistenceContext = session.PersistenceContext; EntityEntry entry = persistenceContext.RemoveEntry(instance); if (entry == null) { throw new AssertionFailure("Possible nonthreadsafe access to session"); } entry.PostDelete(); EntityKey key = new EntityKey(entry.Id, entry.Persister, session.EntityMode); persistenceContext.RemoveEntity(key); persistenceContext.RemoveProxy(key); if (persister.HasCache) { persister.Cache.Evict(ck); } PostDelete(); if (statsEnabled && !veto) { stopwatch.Stop(); Session.Factory.StatisticsImplementor.DeleteEntity(Persister.EntityName, stopwatch.Elapsed); } }
/// <summary> /// Unsupported! /// </summary> public void Release(CacheKey key, ISoftLock @lock) { log.Error("Application attempted to edit read only item: " + key); }
/// <summary> /// Unsupported! /// </summary> public Task <bool> AfterUpdateAsync(CacheKey key, object value, object version, ISoftLock @lock, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <bool>(cancellationToken)); } try { return(Task.FromResult <bool>(AfterUpdate(key, value, version, @lock))); } catch (Exception ex) { return(Task.FromException <bool>(ex)); } }
/// <summary> /// Invalidate the item (again, for safety). /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="version"></param> /// <param name="lock"></param> public void AfterUpdate(object key, object value, object version, ISoftLock @lock) { Release(key, @lock); }