public override async Task ExecuteAfterTransactionCompletionAsync(bool success, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // NH Different behavior: to support unlocking collections from the cache.(r3260) CacheKey ck = Session.GenerateCacheKey(await(GetKeyAsync(cancellationToken)).ConfigureAwait(false), Persister.KeyType, Persister.Role); if (success) { // we can't disassemble a collection if it was uninitialized // or detached from the session if (Collection.WasInitialized && Session.PersistenceContext.ContainsCollection(Collection)) { CollectionCacheEntry entry = await(CollectionCacheEntry.CreateAsync(Collection, Persister, cancellationToken)).ConfigureAwait(false); bool put = await(Persister.Cache.AfterUpdateAsync(ck, entry, null, Lock, cancellationToken)).ConfigureAwait(false); if (put && Session.Factory.Statistics.IsStatisticsEnabled) { Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); } } } else { await(Persister.Cache.ReleaseAsync(ck, Lock, cancellationToken)).ConfigureAwait(false); } }
/// <summary> Try to initialize a collection from the cache</summary> private async Task <bool> InitializeCollectionFromCacheAsync(object id, ICollectionPersister persister, IPersistentCollection collection, ISessionImplementor source, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (!(source.EnabledFilters.Count == 0) && persister.IsAffectedByEnabledFilters(source)) { log.Debug("disregarding cached version (if any) of collection due to enabled filters "); return(false); } bool useCache = persister.HasCache && source.CacheMode.HasFlag(CacheMode.Get); if (!useCache) { return(false); } else { ISessionFactoryImplementor factory = source.Factory; CacheKey ck = source.GenerateCacheKey(id, persister.KeyType, persister.Role); object ce = await(persister.Cache.GetAsync(ck, source.Timestamp, cancellationToken)).ConfigureAwait(false); if (factory.Statistics.IsStatisticsEnabled) { if (ce == null) { factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); } else { factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); } } if (ce == null) { log.Debug("Collection cache miss: {0}", ck); } else { log.Debug("Collection cache hit: {0}", ck); } if (ce == null) { return(false); } else { IPersistenceContext persistenceContext = source.PersistenceContext; CollectionCacheEntry cacheEntry = (CollectionCacheEntry)persister.CacheEntryStructure.Destructure(ce, factory); await(cacheEntry.AssembleAsync(collection, persister, persistenceContext.GetCollectionOwner(id, persister), cancellationToken)).ConfigureAwait(false); persistenceContext.GetCollectionEntry(collection).PostInitialize(collection); return(true); } } }
public override void AfterTransactionCompletion(bool success) { // NH Different behavior: to support unlocking collections from the cache.(r3260) if (Persister.HasCache) { CacheKey ck = new CacheKey(Key, Persister.KeyType, Persister.Role, Session.EntityMode, Session.Factory); if (success) { // we can't disassemble a collection if it was uninitialized // or detached from the session if (Collection.WasInitialized && Session.PersistenceContext.ContainsCollection(Collection)) { CollectionCacheEntry entry = new CollectionCacheEntry(Collection, Persister); bool put = Persister.Cache.AfterUpdate(ck, entry, null, Lock); if (put && Session.Factory.Statistics.IsStatisticsEnabled) { Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName); } } } else { Persister.Cache.Release(ck, Lock); } } }
protected void AssertEqual(CollectionCacheEntry original, CollectionCacheEntry copy) { Assert.That(copy.State, Is.TypeOf(original.State.GetType())); var originalArray = original.State; var copyArray = copy.State; for (var i = 0; i < copyArray.Length; i++) { if (originalArray[i] == null) { Assert.That(copyArray[i], Is.Null); continue; } Assert.That(copyArray[i], Is.TypeOf(originalArray[i].GetType())); if (originalArray[i] is AnyType.ObjectTypeCacheEntry originalAnyType) { var copyAnyType = (AnyType.ObjectTypeCacheEntry)copyArray[i]; AssertEqual(originalAnyType, copyAnyType); } else { Assert.That(copyArray[i], Is.EqualTo(originalArray[i])); } } }
/// <summary> Try to initialize a collection from the cache</summary> private bool InitializeCollectionFromCache(object id, ICollectionPersister persister, IPersistentCollection collection, ISessionImplementor source) { if (!(source.EnabledFilters.Count == 0) && persister.IsAffectedByEnabledFilters(source)) { log.Debug("disregarding cached version (if any) of collection due to enabled filters "); return(false); } bool useCache = persister.HasCache && ((source.CacheMode & CacheMode.Get) == CacheMode.Get); if (!useCache) { return(false); } else { ISessionFactoryImplementor factory = source.Factory; CacheKey ck = new CacheKey(id, persister.KeyType, persister.Role, source.EntityMode, factory); object ce = persister.Cache.Get(ck, source.Timestamp); if (factory.Statistics.IsStatisticsEnabled) { if (ce == null) { factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); } else { factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); } } if (ce == null) { log.DebugFormat("Collection cache miss: {0}", ck); } else { log.DebugFormat("Collection cache hit: {0}", ck); } if (ce == null) { return(false); } else { IPersistenceContext persistenceContext = source.PersistenceContext; CollectionCacheEntry cacheEntry = (CollectionCacheEntry)persister.CacheEntryStructure.Destructure(ce, factory); cacheEntry.Assemble(collection, persister, persistenceContext.GetCollectionOwner(id, persister)); persistenceContext.GetCollectionEntry(collection).PostInitialize(collection); return(true); } } }
public void SecondLevelCachedCollectionsFiltering() { TestData testData = new TestData(this); testData.Prepare(); ISession session = OpenSession(); // Force a collection into the second level cache, with its non-filtered elements Salesperson sp = (Salesperson)session.Load(typeof(Salesperson), testData.steveId); NHibernateUtil.Initialize(sp.Orders); ICollectionPersister persister = ((ISessionFactoryImplementor)sessions) .GetCollectionPersister(typeof(Salesperson).FullName + ".Orders"); Assert.IsTrue(persister.HasCache, "No cache for collection"); CacheKey cacheKey = new CacheKey(testData.steveId, persister.KeyType, persister.Role, EntityMode.Poco, (ISessionFactoryImplementor)sessions); CollectionCacheEntry cachedData = (CollectionCacheEntry)persister.Cache.Cache.Get(cacheKey); Assert.IsNotNull(cachedData, "collection was not in cache"); session.Close(); session = OpenSession(); session.EnableFilter("fulfilledOrders").SetParameter("asOfDate", testData.lastMonth); sp = (Salesperson)session.CreateQuery("from Salesperson as s where s.id = :id") .SetInt64("id", testData.steveId) .UniqueResult(); Assert.AreEqual(1, sp.Orders.Count, "Filtered-collection not bypassing 2L-cache"); CollectionCacheEntry cachedData2 = (CollectionCacheEntry)persister.Cache.Cache.Get(cacheKey); Assert.IsNotNull(cachedData2, "collection no longer in cache!"); Assert.AreSame(cachedData, cachedData2, "Different cache values!"); session.Close(); session = OpenSession(); session.EnableFilter("fulfilledOrders").SetParameter("asOfDate", testData.lastMonth); sp = (Salesperson)session.Load(typeof(Salesperson), testData.steveId); Assert.AreEqual(1, sp.Orders.Count, "Filtered-collection not bypassing 2L-cache"); session.Close(); // Finally, make sure that the original cached version did not get over-written session = OpenSession(); sp = (Salesperson)session.Load(typeof(Salesperson), testData.steveId); Assert.AreEqual(2, sp.Orders.Count, "Actual cached version got over-written"); session.Close(); testData.Release(); }
/// <summary> Add the collection to the second-level cache </summary> /// <param name="lce">The entry representing the collection to add </param> /// <param name="persister">The persister </param> /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param> private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollectionPersister persister, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ISessionImplementor session = LoadContext.PersistenceContext.Session; ISessionFactoryImplementor factory = session.Factory; if (log.IsDebugEnabled()) { log.Debug("Caching collection: {0}", MessageHelper.CollectionInfoString(persister, lce.Collection, lce.Key, session)); } if (!(session.EnabledFilters.Count == 0) && persister.IsAffectedByEnabledFilters(session)) { // some filters affecting the collection are enabled on the session, so do not do the put into the cache. log.Debug("Refusing to add to cache due to enabled filters"); // todo : add the notion of enabled filters to the CacheKey to differentiate filtered collections from non-filtered; // but CacheKey is currently used for both collections and entities; would ideally need to define two separate ones; // currently this works in conjunction with the check on // DefaultInitializeCollectionEventHandler.initializeCollectionFromCache() (which makes sure to not read from // cache with enabled filters). return; // EARLY EXIT!!!!! } IComparer versionComparator; object version; if (persister.IsVersioned) { versionComparator = persister.OwnerEntityPersister.VersionType.Comparator; object collectionOwner = LoadContext.PersistenceContext.GetCollectionOwner(lce.Key, persister); if (collectionOwner == null) { return; } version = LoadContext.PersistenceContext.GetEntry(collectionOwner).Version; } else { version = null; versionComparator = null; } CollectionCacheEntry entry = new CollectionCacheEntry(lce.Collection, persister); CacheKey cacheKey = session.GenerateCacheKey(lce.Key, persister.KeyType, persister.Role); bool put = await(persister.Cache.PutAsync(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, versionComparator, factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh, cancellationToken)).ConfigureAwait(false); if (put && factory.Statistics.IsStatisticsEnabled) { factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName); } }
public static void SaveCollection <T>(string sKey, List <T> colItems) where T : IUniqueIdActiveRecord { CollectionCacheEntry objCacheEntry = new CollectionCacheEntry(); objCacheEntry.Key = sKey; objCacheEntry.CacheEntry = DateTime.Now; objCacheEntry.LastAccess = DateTime.Now; objCacheEntry.LastUpdate = DateTime.Now; objCacheEntry.Collection = CloneCollection(colItems); lock (GetKeyLock(sKey)) { _htCollectionCache[sKey] = objCacheEntry; } }
public static void SaveCollection(string sKey, List <Guid> colIDs) { CollectionCacheEntry objCacheEntry = new CollectionCacheEntry(); objCacheEntry.Key = sKey; objCacheEntry.CacheEntry = DateTime.Now; objCacheEntry.LastAccess = DateTime.Now; objCacheEntry.LastUpdate = DateTime.Now; objCacheEntry.Collection = CloneCollection(colIDs); lock (GetKeyLock(sKey)) { _htCollectionCache[sKey] = objCacheEntry; } }
private async Task <bool> AssembleAsync( CacheKey ck, object ce, ICollectionPersister persister, ISessionImplementor source, IPersistentCollection collection, object collectionKey, bool alterStatistics, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ISessionFactoryImplementor factory = source.Factory; if (factory.Statistics.IsStatisticsEnabled && alterStatistics) { if (ce == null) { factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); } else { factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); } } if (ce == null) { log.Debug("Collection cache miss: {0}", ck); } else { log.Debug("Collection cache hit: {0}", ck); } if (ce == null) { return(false); } else { IPersistenceContext persistenceContext = source.PersistenceContext; CollectionCacheEntry cacheEntry = (CollectionCacheEntry)persister.CacheEntryStructure.Destructure(ce, factory); await(cacheEntry.AssembleAsync(collection, persister, persistenceContext.GetCollectionOwner(collectionKey, persister), cancellationToken)).ConfigureAwait(false); persistenceContext.GetCollectionEntry(collection).PostInitialize(collection, persistenceContext); if (collection.HasQueuedOperations) { collection.ApplyQueuedOperations(); } return(true); } }
public static void UpdateItem <T>(string sKey, T objItem) where T : IUniqueIdActiveRecord { lock (GetKeyLock(sKey)) { if (_htCollectionCache.ContainsKey(sKey) == true) { CollectionCacheEntry objCacheEntry = _htCollectionCache[sKey]; List <T> colItems = (List <T>)objCacheEntry.Collection; colItems.RemoveAll(o => o.Id == objItem.Id); colItems.Add(objItem); objCacheEntry.Collection = colItems; objCacheEntry.LastAccess = DateTime.Now; objCacheEntry.LastUpdate = DateTime.Now; } } }
public async Task SecondLevelCachedCollectionsFilteringAsync() { var persister = Sfi .GetCollectionPersister(typeof(Salesperson).FullName + ".Orders"); var cacheKey = new CacheKey(testData.steveId, persister.KeyType, persister.Role, Sfi); CollectionCacheEntry cachedData; using (var session = OpenSession()) { // Force a collection into the second level cache, with its non-filtered elements var sp = (Salesperson)await(session.LoadAsync(typeof(Salesperson), testData.steveId)); await(NHibernateUtil.InitializeAsync(sp.Orders)); Assert.IsTrue(persister.HasCache, "No cache for collection"); cachedData = (CollectionCacheEntry)await(persister.Cache.Cache.GetAsync(cacheKey, CancellationToken.None)); Assert.IsNotNull(cachedData, "collection was not in cache"); } using (var session = OpenSession()) { session.EnableFilter("fulfilledOrders").SetParameter("asOfDate", testData.lastMonth); var sp = (Salesperson)await(session.CreateQuery("from Salesperson as s where s.id = :id") .SetInt64("id", testData.steveId) .UniqueResultAsync()); Assert.AreEqual(1, sp.Orders.Count, "Filtered-collection not bypassing 2L-cache"); CollectionCacheEntry cachedData2 = (CollectionCacheEntry)await(persister.Cache.Cache.GetAsync(cacheKey, CancellationToken.None)); Assert.IsNotNull(cachedData2, "collection no longer in cache!"); Assert.AreSame(cachedData, cachedData2, "Different cache values!"); } using (var session = OpenSession()) { session.EnableFilter("fulfilledOrders").SetParameter("asOfDate", testData.lastMonth); var sp = (Salesperson)await(session.LoadAsync(typeof(Salesperson), testData.steveId)); Assert.AreEqual(1, sp.Orders.Count, "Filtered-collection not bypassing 2L-cache"); } // Finally, make sure that the original cached version did not get over-written using (var session = OpenSession()) { var sp = (Salesperson)await(session.LoadAsync(typeof(Salesperson), testData.steveId)); Assert.AreEqual(2, sp.Orders.Count, "Actual cached version got over-written"); } }
private static List <T> GetCollection <T>(string sKey) where T : IUniqueIdActiveRecord { List <T> objReturnCollection = null; if (_htCollectionCache.Keys.Contains(sKey) == true) { CollectionCacheEntry objCacheEntry = null; lock (GetKeyLock(sKey)) { objCacheEntry = _htCollectionCache[sKey]; objCacheEntry.LastAccess = DateTime.Now; } if (objCacheEntry.Collection != null && objCacheEntry.Collection is List <T> ) { objReturnCollection = CloneCollection <T>((List <T>)objCacheEntry.Collection); } } return(objReturnCollection); }
private bool Assemble(CacheKey ck, object ce, ICollectionPersister persister, ISessionImplementor source, IPersistentCollection collection, object id, bool alterStatistics) { ISessionFactoryImplementor factory = source.Factory; if (factory.Statistics.IsStatisticsEnabled && alterStatistics) { if (ce == null) { factory.StatisticsImplementor.SecondLevelCacheMiss(persister.Cache.RegionName); } else { factory.StatisticsImplementor.SecondLevelCacheHit(persister.Cache.RegionName); } } if (ce == null) { log.Debug("Collection cache miss: {0}", ck); } else { log.Debug("Collection cache hit: {0}", ck); } if (ce == null) { return(false); } else { IPersistenceContext persistenceContext = source.PersistenceContext; CollectionCacheEntry cacheEntry = (CollectionCacheEntry)persister.CacheEntryStructure.Destructure(ce, factory); cacheEntry.Assemble(collection, persister, persistenceContext.GetCollectionOwner(id, persister)); persistenceContext.GetCollectionEntry(collection).PostInitialize(collection, persistenceContext); return(true); } }
private void CheckCollectionCacheEntry(CollectionCacheEntry original, CollectionCacheEntry copy) { Assert.That(copy.State, Is.TypeOf(original.State.GetType())); var originalArray = original.State; var copyArray = copy.State; for (var i = 0; i < copyArray.Length; i++) { Assert.That(copyArray[i], Is.TypeOf(originalArray[i].GetType())); if (originalArray[i] is AnyType.ObjectTypeCacheEntry originalAnyType) { var copyAnyType = (AnyType.ObjectTypeCacheEntry)copyArray[i]; CheckObjectTypeCacheEntry(originalAnyType, copyAnyType); } else { Assert.That(copyArray[i], Is.EqualTo(originalArray[i])); } } }
public static List <Guid> FetchAndCache(string sKey, Func <List <Guid> > fGetCollectionDelegate) { List <Guid> colIds = new List <Guid>(); lock (GetKeyLock(sKey)) { if (_htCollectionCache.Keys.Contains(sKey) == true) { CollectionCacheEntry objCacheEntry = _htCollectionCache[sKey]; colIds = (List <Guid>)objCacheEntry.Collection; objCacheEntry.LastAccess = DateTime.Now; } else { colIds = fGetCollectionDelegate(); SaveCollection(sKey, colIds); } } List <Guid> colReturnIds = CloneCollection(colIds); return(colReturnIds); }
public static List <T> FetchAndCache <T>(string sKey, Func <List <T> > fGetCollectionDelegate) where T : IUniqueIdActiveRecord { List <T> colItems = new List <T>(); lock (GetKeyLock(sKey)) { if (_htCollectionCache.Keys.Contains(sKey) == true) { CollectionCacheEntry objCacheEntry = _htCollectionCache[sKey]; colItems = (List <T>)objCacheEntry.Collection; objCacheEntry.LastAccess = DateTime.Now; } else { colItems = fGetCollectionDelegate(); SaveCollection <T>(sKey, colItems); } } List <T> objReturnCollection = CloneCollection <T>(colItems); return(objReturnCollection); }
public static void UpdateCollection <T>(string sKey, List <T> colItems) where T : IUniqueIdActiveRecord { lock (GetKeyLock(sKey)) { if (_htCollectionCache.ContainsKey(sKey) == true) { CollectionCacheEntry objCacheEntry = _htCollectionCache[sKey]; objCacheEntry.LastAccess = DateTime.Now; objCacheEntry.LastUpdate = DateTime.Now; objCacheEntry.Collection = new List <T>(); //Clone the collection before insertion to ensure it can't be touched foreach (T objItem in colItems) { objCacheEntry.Collection.Add(objItem); } _htCollectionCache[sKey] = objCacheEntry; } else { SaveCollection <T>(sKey, colItems); } } }