public void CacheHistoryWorking() { var rand = new Random(); const int max = 200; // our cache size covers 20% of our key space const int refetches = 5; var innerFactory = new CacheFactory() { CacheName = "inner" }; var fac0 = new StochasticCacheFactory() { MaxSize = max, NumHistoryFetches = 0, Inner = innerFactory }; var fac5 = new StochasticCacheFactory() { MaxSize = max, NumHistoryFetches = refetches, Inner = innerFactory }; var lruFac = new LruCacheFactory() { MaxSize = max, Inner = innerFactory }; var cache0 = fac0.Create <int, int>("StochasticCacheTest"); var cache5 = fac5.Create <int, int>("StochasticCacheTest"); var lru = lruFac.Create <int, int>("StochasticCacheTest"); var cache0Count = 0; var cache5Count = 0; var lruCount = 0; for (int i = 0; i < 100000; i++) { var index = i % 5 != 0 ? rand.Next(200) : rand.Next(1000); // 80 % of hits come out of 20% of the key space. cache0.GetOrAdd(index, (key) => { cache0Count++; return(index); }); cache5.GetOrAdd(index, (key) => { cache5Count++; return(index); }); lru.GetOrAdd(index, (key) => { lruCount++; return(index); }); } Console.WriteLine("Cache misses, smaller is better 0: {0}, {1}: {2}, lru: {3}", cache0Count, refetches, cache5Count, lruCount); Assert.That(cache0Count, Is.GreaterThan(cache5Count)); // It is possible but very very unlikley that this will fail. }
/// <summary> /// Create cache, according to the configuration of the factory. /// </summary> /// <typeparam name="TKey">Type of cache key.</typeparam> /// <typeparam name="TValue">Type of cache value.</typeparam> /// <param name="cacheName">Name of the cache.</param> /// <returns> /// A cache. /// </returns> /// <exception cref="System.InvalidOperationException"></exception> public ICache <TKey, TValue> Create <TKey, TValue>(string cacheName) { ICacheFactory fact = null; // // The first caches encountered will be lowest in the stack // if (Redis) { if (ConfigurationSettings.GetCacheConfigurationSection().RedisCacheSettings.Enabled) { fact = new RedisCacheFactory { Inner = null, CompressKey = RedisKeyCompression, CompressValue = RedisValueCompression, KeyExpiry = RedisKeyExpiry }; } else { Diagnostics.EventLog.Application.WriteWarning("Cache '{0}' requested a Redis layer but was denied due to the SoftwarePlatform.config settings.", cacheName); } } if (Dictionary) { fact = new DictionaryCacheFactory { Inner = fact }; } if (MaxCacheEntries > 0) { // Attempt to get the maximum value from the configuration settings int configMaxCacheSize = GetMaximumCacheSize(cacheName); if (configMaxCacheSize > 0) { MaxCacheEntries = configMaxCacheSize; } if (Lru) { fact = new LruCacheFactory { Inner = fact, MaxSize = MaxCacheEntries, EvictionFrequency = LruEvictionFrequency }; } else { fact = new StochasticCacheFactory { Inner = fact, MaxSize = MaxCacheEntries }; } } if (ExpirationInterval != TimeSpan.Zero) { fact = new TimeoutCacheFactory { Inner = fact, Expiration = ExpirationInterval, EvictionFrequency = TimeoutEvictionFrequency }; } if (TransactionAware) { fact = new TransactionAwareCacheFactory { Inner = fact }; } if (Logging) { fact = new LoggingCacheFactory { Inner = fact, MaxSize = MaxCacheEntries }; } if (fact != null && (ThreadSafe && !fact.ThreadSafe)) { fact = new ThreadSafeCacheFactory { Inner = fact }; } if (BlockIfPending || DelayedInvalidates) { fact = new BlockIfPendingCacheFactory { Inner = fact }; } if (DelayedInvalidates) { // Must be below BlockIfPending fact = new DelayedInvalidateCacheFactory { Inner = fact, ExpirationInterval = new TimeSpan(0, 1, 0) }; } if (MetadataCache) { fact = new MetadataCacheFactory { Inner = fact }; } if (IsolateTenants) { fact = new PerTenantNonSharingCacheFactory { Inner = fact }; } if (Distributed) { fact = new RedisPubSubCacheFactory <TKey>(fact, IsolateTenants); } // Final safety check if (fact == null || (ThreadSafe && !fact.ThreadSafe)) { throw new InvalidOperationException(); } var cache = fact.Create <TKey, TValue>(cacheName); return(cache); }