public static TriggerAsynchronousFetchStatus TriggerAsynchronousFetch <T>(LoadSerializedObjectDelegate <T> loadObject, string cacheItemName , string iterationKey, CacheItemContainer cacheItemContainer) where T : class { ContinuousCacheAccessThreadHelper <T> threadHelper; Thread thread; // we are in the extended lifespan, so we need to check whether we have to reload the object if (ContinuousCacheAccessSynchronizationManager.SetIsFetchingDataFlagToTrue(cacheItemContainer.CacheKey)) { #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "TRIGGER ASYNCHRONOUS FETCH: Initialize asynchronous fetch"); #endif #endregion threadHelper = ContinuousCacheAccessThreadHelper <T> .GetInstance(loadObject, cacheItemName, iterationKey); thread = new Thread(threadHelper.FetchAndInsertData); thread.Start(); return(TriggerAsynchronousFetchStatus.SucessfullyInitialized); } else { #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "TRIGGER ASYNCHRONOUS FETCH: Already in process..."); #endif #endregion return(TriggerAsynchronousFetchStatus.AlreadyFetching); } }
/// <summary> /// Gets the instance. /// </summary> /// <param name="loadObject">The load object.</param> /// <param name="cacheItemName">Name of the cache item.</param> /// <param name="iterationKey">The iteration key.</param> /// <returns></returns> internal static ContinuousCacheAccessThreadHelper <T> GetInstance(LoadSerializedObjectDelegate <T> loadObject, string cacheItemName, string iterationKey) { ContinuousCacheAccessThreadHelper <T> record = new ContinuousCacheAccessThreadHelper <T>(); record._LoadObjectDelegate = loadObject; record._CacheItemName = cacheItemName; record._IterationKey = iterationKey ?? string.Empty; return(record); }
/// <summary> /// Loads from cache. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="cacheItemName">Name of the cache item.</param> /// <param name="iterationKey">The iteration key.</param> /// <param name="loadObject">The load object.</param> /// <returns></returns> public static T LoadFromCache <T>(string cacheItemName, string iterationKey, LoadSerializedObjectDelegate <T> loadObject) where T : class { T returnObject = default(T); // get the section CacheItemContainer cacheItemContainer = CreateSaveItemInstance(cacheItemName); string cacheKey = cacheItemContainer.CacheKey + iterationKey ?? string.Empty; if (_StaticCacheController.Enabled && cacheItemContainer.CacheItem.Enabled) {// use cache #region InProcess returnObject = _Cache[cacheKey] as T; if (returnObject == null) { lock (ContinuousCacheAccessSynchronizationManager.CacheItemNameLocks[cacheItemName]) { // check again in case the object was inserted into cache after the conditional statement but before the Monitor lock returnObject = _Cache[cacheKey] as T; if (returnObject == null) {// get new object and cache it #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "Item not found at cache"); #endif #endregion try { returnObject = loadObject.Invoke(); } catch (Exception e) { throw new CachingException("An error occurred during the execution of the loadObject delegate. See the inner exception for further details.", e); } if (returnObject != null) { cacheItemContainer.LatestFetch = DateTime.UtcNow; CacheItemPolicy cacheItemPolicy = new CacheItemPolicy(); cacheItemPolicy.Priority = cacheItemContainer.CacheItem.CacheItemPriority; cacheItemPolicy.AbsoluteExpiration = cacheItemContainer.ActualExpiryDate; SlimCacheManager._Cache.Set( cacheKey , returnObject , cacheItemPolicy); #region debug #if DEBUG Trace.WriteLine("\n" + DebugConstants.DEBUG_PREFIX + "\n*INPROCESS CACHE INSERT:\n*\t\t\tInserted new object into cache at " + cacheItemContainer.LatestFetch.ToString("HH:mm:ss.fff") + "\n*\t\t\tActual expiry date: " + cacheItemContainer.ActualExpiryDate.ToString("HH:mm:ss.fff") + "\n*\t\t\tInserted new stale key with expiry date: " + cacheItemContainer.SpecifiedExpiryDate.ToString("HH:mm:ss.fff") + "\n*\t\t\tKey: " + cacheItemContainer.CacheKey + "\n************************************************************************************************\n"); #endif #endregion } #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "Item inserted at normal cache call"); #endif #endregion } } } else { if (cacheItemContainer.CacheItem.UseContinuousAccess && cacheItemContainer.IsInExtendedLifeSpan(DateTime.UtcNow)) { #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "IsInExtendedLifeSpan at " + DateTime.UtcNow.ToString("HH:mm:ss.fff")); #endif #endregion // we are in the extended lifespan, so we need to check whether we have to reload the object ContinuousCacheAccessSynchronizationManager.TriggerAsynchronousFetch <T>(loadObject, cacheItemName, iterationKey, cacheItemContainer); } } #endregion } else {// don't use cache try { returnObject = loadObject.Invoke(); } catch (Exception e) { throw new CachingException("An error occurred during the execution of the loadObject delegate. See the inner exception for further details.", e); } } return(returnObject); }
/// <summary> /// Loads from cache. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="cacheItemName">Name of the cache item.</param> /// <param name="loadObject">The load object.</param> /// <returns></returns> public static T LoadFromCache <T>(string cacheItemName, LoadSerializedObjectDelegate <T> loadObject) where T : class { return(LoadFromCache <T>(cacheItemName, null, loadObject)); }
public static TriggerAsynchronousFetchStatus TriggerAsynchronousFetch <T>(LoadSerializedObjectDelegate <T> loadObject, string cacheItemName , string iterationKey, CacheItemContainer cacheItemContainer) where T : class { ContinuousCacheAccessThreadHelper <T> threadHelper; Thread thread; switch (cacheItemContainer.CacheMode) { case CacheMode.InProcess: // we are in the extended lifespan, so we need to check whether we have to reload the object if (ContinuousCacheAccessSynchronizationManager.SetIsFetchingDataFlagToTrue(cacheItemContainer.CacheKey)) { #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "TRIGGER ASYNCHRONOUS FETCH: Initialize asynchronous fetch"); #endif #endregion threadHelper = ContinuousCacheAccessThreadHelper <T> .GetInstance(loadObject, cacheItemName, iterationKey); thread = new Thread(threadHelper.FetchAndInsertData); thread.Start(); return(TriggerAsynchronousFetchStatus.SucessfullyInitialized); } else { #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "TRIGGER ASYNCHRONOUS FETCH: Already in process..."); #endif #endregion return(TriggerAsynchronousFetchStatus.AlreadyFetching); } case CacheMode.Memcached: case CacheMode.MemcachedProtocolBufferSerialization: DateTime temporaryExpiry = DateTime.UtcNow.Add(cacheItemContainer.CacheItem.LifeSpan); #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "TRIGGER ASYNCHRONOUS FETCH MEMCACHED: Try to add stale key with temporary expiry date " + temporaryExpiry.ToString("HH:mm:ss.fff")); #endif #endregion if (DistributedCache.Add(cacheItemContainer.MemcachedStaleCacheKey, new object(), temporaryExpiry)) { // great, we have to reload now #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "TRIGGER ASYNCHRONOUS FETCH MEMCACHED: Initialize asynchronous fetch"); #endif #endregion threadHelper = ContinuousCacheAccessThreadHelper <T> .GetInstance(loadObject, cacheItemName, iterationKey); thread = new Thread(threadHelper.FetchAndInsertMemcachedData); thread.Start(); return(TriggerAsynchronousFetchStatus.SucessfullyInitialized); } else { #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "TRIGGER ASYNCHRONOUS FETCH MEMCACHED: Already in process..."); #endif #endregion return(TriggerAsynchronousFetchStatus.AlreadyFetching); } default: throw new ArgumentException("CacheMode " + cacheItemContainer.CacheMode + " not allowed"); } }
/// <summary> /// Loads the object from cache. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name">The name.</param> /// <param name="iterationKey">The iteration key.</param> /// <param name="loadObject">The load object.</param> /// <returns></returns> private static T LoadObjectFromCache <T>(string name, string iterationKey, LoadSerializedObjectDelegate <T> loadObject) { T returnObject = default(T); // get the section ICacheController section = CacheControllerSection; ICacheItem item = CreateSaveItemInstance(section, name, iterationKey); string cacheKey = GetCacheKey(item, iterationKey); if (section.Enabled && item.Enabled) {// use cache if (item.IsClustered) { try { returnObject = (T)ClusteredCacheManager.GetCacheItem( cacheKey , delegate { return(loadObject()); } , item.CacheItemPriority , true); } catch (Exception e) { throw new CachingException("An error occurred during the retrieval of the business object from the cache collection. See the inner exception for further details.", e); } } else { lock (_CacheLock) { if (HttpRuntime.Cache[cacheKey] == null) {// get new object and cache it try { if (item.Minutes < 0 && item.Seconds < 0) { throw new CachingException("You must specify either a seconds or minutes value for a non clusterd cache item."); } DateTime expire = item.Seconds < 0 ? DateTime.UtcNow.AddMinutes(item.Minutes) : DateTime.UtcNow.AddSeconds(item.Seconds); returnObject = loadObject(); if (returnObject != null) { HttpRuntime.Cache.Remove(cacheKey); HttpRuntime.Cache.Insert(cacheKey, returnObject, null, expire, System.Web.Caching.Cache.NoSlidingExpiration, item.CacheItemPriority, null); } } catch (Exception e) { throw new CachingException("An error occurred during the execution of the loadObject delegate. See the inner exception for further details.", e); } } else {// get object from cache try { returnObject = (T)HttpRuntime.Cache[cacheKey]; } catch (Exception e) { throw new CachingException("An error occurred during the retrieval of the business object from the cache collection. See the inner exception for further details.", e); } } } } } else {// don't use cache try { returnObject = loadObject(); } catch (Exception e) { throw new CachingException("An error occurred during the execution of the loadObject delegate. See the inner exception for further details.", e); } } return(returnObject); }
/// <summary> /// Loads from cache. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name">The name.</param> /// <param name="iterationKey">The iteration key.</param> /// <param name="loadObject">The load object.</param> /// <returns></returns> public static T LoadFromCache <T>(string name, string iterationKey, LoadSerializedObjectDelegate <T> loadObject) { return(LoadObjectFromCache <T>(name, iterationKey, loadObject)); }
/// <summary> /// Loads from cache. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name">The name.</param> /// <param name="loadObject">The load object.</param> /// <returns></returns> public static T LoadFromCache <T>(string name, LoadSerializedObjectDelegate <T> loadObject) { return(LoadObjectFromCache <T>(name, null, loadObject)); }
/// <summary> /// Loads from cache. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="cacheItemName">Name of the cache item.</param> /// <param name="iterationKey">The iteration key.</param> /// <param name="loadObject">The load object.</param> /// <returns></returns> public static T LoadFromCache <T>(string cacheItemName, string iterationKey, LoadSerializedObjectDelegate <T> loadObject) where T : class { T returnObject = default(T); // get the section CacheItemContainer cacheItemContainer = CreateSaveItemInstance(cacheItemName); string cacheKey = cacheItemContainer.CacheKey + iterationKey ?? string.Empty; if (_StaticCacheController.Enabled && cacheItemContainer.CacheItem.Enabled) {// use cache switch (cacheItemContainer.CacheMode) { case CacheMode.InProcess: #region InProcess returnObject = HttpRuntime.Cache[cacheKey] as T; if (returnObject == null) { lock (ContinuousCacheAccessSynchronizationManager.CacheItemNameLocks[cacheItemName]) { // check again in case the object was inserted into cache after the conditional statement but before the Monitor lock returnObject = HttpRuntime.Cache[cacheKey] as T; if (returnObject == null) { // get new object and cache it #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "Item not found at cache"); #endif #endregion try { returnObject = loadObject.Invoke(); } catch (Exception e) { throw new CachingException("An error occurred during the execution of the loadObject delegate. See the inner exception for further details.", e); } if (returnObject != null) { cacheItemContainer.LatestFetch = DateTime.UtcNow; HttpRuntime.Cache.Insert( cacheKey , returnObject , null , cacheItemContainer.ActualExpiryDate , System.Web.Caching.Cache.NoSlidingExpiration , cacheItemContainer.CacheItem.CacheItemPriority , null); #region debug #if DEBUG Trace.WriteLine("\n" + DebugConstants.DEBUG_PREFIX + "\n*INPROCESS CACHE INSERT:\n*\t\t\tInserted new object into cache at " + cacheItemContainer.LatestFetch.ToString("HH:mm:ss.fff") + "\n*\t\t\tActual expiry date: " + cacheItemContainer.ActualExpiryDate.ToString("HH:mm:ss.fff") + "\n*\t\t\tInserted new stale key with expiry date: " + cacheItemContainer.SpecifiedExpiryDate.ToString("HH:mm:ss.fff") + "\n*\t\t\tKey: " + cacheItemContainer.CacheKey + "\n************************************************************************************************\n"); #endif #endregion } #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "Item inserted at normal cache call"); #endif #endregion } } } else { if (cacheItemContainer.CacheItem.UseContinuousAccess && cacheItemContainer.IsInExtendedLifeSpan(DateTime.UtcNow)) { #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "IsInExtendedLifeSpan at " + DateTime.UtcNow.ToString("HH:mm:ss.fff")); #endif #endregion // we are in the extended lifespan, so we need to check whether we have to reload the object ContinuousCacheAccessSynchronizationManager.TriggerAsynchronousFetch <T>(loadObject, cacheItemName, iterationKey, cacheItemContainer); } } #endregion break; case CacheMode.Memcached: case CacheMode.MemcachedProtocolBufferSerialization: #region Memcached returnObject = GetMemcachedObject <T>(cacheKey, cacheItemContainer.CacheMode); if (returnObject == null) { lock (ContinuousCacheAccessSynchronizationManager.CacheItemNameLocks[cacheItemName]) { // check again in case the object was inserted into cache after the conditional statement but before the Monitor lock returnObject = GetMemcachedObject <T>(cacheKey, cacheItemContainer.CacheMode); if (returnObject == null) { // get new object and cache it #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "Item not found at cache"); #endif #endregion try { returnObject = loadObject.Invoke(); } catch (Exception e) { throw new CachingException("An error occurred during the execution of the loadObject delegate. See the inner exception for further details.", e); } if (returnObject != null) { InsertMemcachedObject <T>(cacheItemContainer, returnObject); } #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "Item inserted at normal cache call"); #endif #endregion } } } else { #region Note on Memcached locking and synchronization /* Important note on memcached caching: * * trying to accomplish thread safety via Memcached.Add is not thread safe and may not work as expected. Memcached.Add should only * add an object if it does not exist yet, however, you never know when memcached actually inserts the data. If you write * code like -> * * int i = 5; * DistributedCache.Insert("MyKey", 5); * int cachedValue = DistributedCache.Get<int>("MyKey"); * * it is not guaranteed that "cachedValue" will be 5. During testing against a dev machine with memcache running as a * Windows XP service, it took up to 600ms until a value was actually retrievable after the insert. Here s a table with * tests which were run against the dev machine: * * ,--. ,---. Add Test success percentage with 1 milliseconds: 0.3942 * / '. / \ Add Test success percentage with 26 milliseconds: 0.416 \ ; Add Test success percentage with 51 milliseconds: 0.4408 \-| Add Test success percentage with 76 milliseconds: 0.4804 \ (o o) (/ Add Test success percentage with 101 milliseconds: 0.555 \ /'v' ,-' Add Test success percentage with 126 milliseconds: 0.5548 \ ,------/ >< \---' Add Test success percentage with 151 milliseconds: 0.5556 \ /) ; -- : Add Test success percentage with 176 milliseconds: 0.5562 \ ,---| ---- |--. Add Test success percentage with 201 milliseconds: 0.6018 \ ; | ---- | : Add Test success percentage with 226 milliseconds: 0.6232 \ (| ,-| ---- |-. |) Add Test success percentage with 251 milliseconds: 0.6 | /| ---- |\ | Add Test success percentage with 276 milliseconds: 0.6132 |/ | ---- | \| Add Test success percentage with 301 milliseconds: 0.6906 \ : ---- ; | Add Test success percentage with 326 milliseconds: 0.738 \ \ -- / / Add Test success percentage with 351 milliseconds: 0.7476 \ ; \ / : Add Test success percentage with 376 milliseconds: 0.76 \ / / \/ \ \ Add Test success percentage with 401 milliseconds: 0.7996 \ /) (\ Add Test success percentage with 426 milliseconds: 0.8068 \ Add Test success percentage with 451 milliseconds: 0.848 \ Add Test success percentage with 476 milliseconds: 0.8486 \ Add Test success percentage with 501 milliseconds: 0.86 \ Add Test success percentage with 526 milliseconds: 0.9284 \ Add Test success percentage with 551 milliseconds: 0.981 \ Add Test success percentage with 576 milliseconds: 0.9554 \ Add Test success percentage with 601 milliseconds: 1 \ Add Test success percentage with 626 milliseconds: 1 \ Add Test success percentage with 651 milliseconds: 1 \ Add Test success percentage with 676 milliseconds: 1 \ Add Test success percentage with 701 milliseconds: 1 \ Add Test success percentage with 726 milliseconds: 1 \ Add Test success percentage with 751 milliseconds: 1 \ Add Test success percentage with 776 milliseconds: 1 \ Add Test success percentage with 801 milliseconds: 1 \ Add Test success percentage with 826 milliseconds: 1 \ Add Test success percentage with 851 milliseconds: 1 \ Add Test success percentage with 876 milliseconds: 1 \ Add Test success percentage with 901 milliseconds: 1 \ Add Test success percentage with 926 milliseconds: 1 \ Add Test success percentage with 951 milliseconds: 1 \ Add Test success percentage with 976 milliseconds: 1 \ \ However, this is still better than aquiring a lock from a database table which would be thread safe but cause an enormous \ amount of SQL traffic. Using a windows service on a dedicated machine which handles the locking would be best but it also \ eats a lot of network bandwidth. \ */ #endregion if (cacheItemContainer.CacheItem.UseContinuousAccess && DistributedCache.Get <object>(cacheItemContainer.MemcachedStaleCacheKey) == null) { #region debug #if DEBUG Trace.WriteLine(DebugConstants.DEBUG_PREFIX + "IsInExtendedLifeSpan at " + DateTime.UtcNow.ToString("HH:mm:ss.fff")); #endif #endregion // we are in the extended lifespan, so we need to check whether we have to reload the object ContinuousCacheAccessSynchronizationManager.TriggerAsynchronousFetch <T>(loadObject, cacheItemName, iterationKey, cacheItemContainer); } } #endregion break; } } else {// don't use cache try { returnObject = loadObject.Invoke(); } catch (Exception e) { throw new CachingException("An error occurred during the execution of the loadObject delegate. See the inner exception for further details.", e); } } return(returnObject); }