How to prevent HttpRuntime.Cache to remove items in ASP.NET 4.5?

有些话、适合烂在心里 提交于 2019-12-25 09:01:54

问题


This is a follow-up to this question, which contains contradictory answers. I am also interested in an answer related to a more recent version of ASP.NET.

My application uses HttpRuntime.Cache to cache some lists of models that should never expire. They are loaded on application warmup and they are changed quite rarely, but read quite often.

My code is the following:

private void ReportRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
    if (!ApplicationPoolService.IsShuttingDown())
    {
        var str = $"Removed cached item with key {key} and count {(value as IDictionary)?.Count}, reason {reason}";
        LoggingService.Log(LogLevel.Info, str);
    }
}

private IDictionary<int, T> ThreadSafeCacheAccessAction(Action<IDictionary<int, T>, bool> action = null)
{
    // refresh cache if necessary
    var dict = HttpRuntime.Cache[CacheDictKey] as IDictionary<int, T>;
    bool invalidated = false;
    if (dict == null)
    {
        lock (CacheLockObject)
        {
            // getting expiration times from model attribute
            var cacheInfo = typeof(T).GetCustomAttributes(typeof(UseInCachedRepositoryAttribute), inherit: true).FirstOrDefault() as UseInCachedRepositoryAttribute;
            int absoluteExpiration = cacheInfo?.AbsoluteExpiration ?? Constants.Cache.IndefiniteRetention;
            int slidingExpiration = cacheInfo?.SlidingExpiration ?? Constants.Cache.NoSlidingExpiration;

            dict = _modelRepository.AllNoTracking.ToList().Where(item => item.PkId != 0).ToDictionary(item => item.PkId, item => item);
            HttpRuntime.Cache.Insert(CacheDictKey, dict, dependencies: null,
                absoluteExpiration: DateTime.Now.AddMinutes(absoluteExpiration),
                slidingExpiration: slidingExpiration <= 0 ? Cache.NoSlidingExpiration : TimeSpan.FromMinutes(slidingExpiration),
                priority: CacheItemPriority.NotRemovable,
                onRemoveCallback: ReportRemovedCallback);

            invalidated = true;
        }
    }

Based on the documentation provided here, I have also included the following markup within the web.config:

<caching>
  <cache disableExpiration="true" disableMemoryCollection="true" />
</caching>

However, from time to time, ReportRemovedCallback is called for item removal. My feeling is that caching configuration from web.config is ignored (the documentation clearly states it is outdated) and that CacheItemPriority.NotRemovable means only "a very high priority", not "never remove".

Question: is there a way to convince HttpRuntime.Cache to never remove some items? Or should I consider another caching mechanism?


回答1:


Ok, so I have dug more and there is no definitive answer, but the following configuration from this old docs seems to apply regardless of trials to deny expiration:

The following default cache element is not explicitly configured in the machine configuration file or in the root Web.config file, but is the default configuration returned by application in the .NET Framework version 2.0.

<cache disableMemoryCollection="false" 
  disableExpiration="false" privateBytesLimit="0" 
  percentagePhysicalMemoryUsedLimit="90" 
  privateBytesPollTime="00:02:00" /> 

So, if physical memory usage is above 90%, it will evict cache items. Since OS tends to use almost all physical memory for system cache (reported by Task Manager), this is not so unlikely as it sounds.

Alternative

I took MemoryCache for a spin, since it is very similar to HttpRuntime.Cache. It provides similar functionality, but lacks the CacheEntryUpdateCallback (you can provide it, but complains with InvalidArgumentException if it is different from null).

Now my code is the following:

var dict = MemoryCache.Default.Get(CacheDictKey) as IDictionary<int, T>;

if (dict == null)
{
    lock (CacheLockObject)
    {
        // getting expiration times from model attribute
        var cacheInfo = typeof(T).GetCustomAttributes(typeof(UseInCachedRepositoryAttribute), inherit: true).FirstOrDefault() as UseInCachedRepositoryAttribute;
        int absoluteExpiration = cacheInfo?.AbsoluteExpiration ?? Constants.Cache.IndefiniteRetention;
        int slidingExpiration = cacheInfo?.SlidingExpiration ?? Constants.Cache.NoSlidingExpiration;

        dict = _modelRepository.AllNoTracking.ToList().Where(item => item.PkId != 0).ToDictionary(item => item.PkId, item => item);

        var cacheItemPolicy = new CacheItemPolicy
        {
            AbsoluteExpiration = DateTime.Now.AddMinutes(absoluteExpiration),
            SlidingExpiration = slidingExpiration <= 0 ? Cache.NoSlidingExpiration : TimeSpan.FromMinutes(slidingExpiration),
            Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable,
            // throws InvalidArgumentException
            // UpdateCallback = CacheEntryUpdateCallback
        };
        MemoryCache.Default.Add(CacheDictKey, dict, cacheItemPolicy);
    }
}

After some tests, there were no extra removals and the memory consumption of w3wp.exe raised as expected.

More details are provided within this answer.



来源:https://stackoverflow.com/questions/43252723/how-to-prevent-httpruntime-cache-to-remove-items-in-asp-net-4-5

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!