I use a System.Runtime.MemoryCache
in a web services integration layer to keep some product lists which are external and slow to retrieve. However, I want to refresh those as they expire to free my callers from waiting on it. For that reason I am prewarming this cache using IIS7.5 and have a RemovedCallback
to reload the data when expired.
However, what happens when the web pool process dies gracefully? MemoryCache is disposable, so it will kick my object out, at which point I'll try to cram a new instance back, whilst putting the entire process on hold. Is there any way for me to safely detect that I should not reload data?
internal class ProductCache {
private static object _lock = new object();
private static string _cid = Guid.NewGuid().ToString();
public static Product[] data
{
get
{
lock (_lock)
{
if (!MemoryCache.Default.Contains(_cid))
{
Product[] p;
try
{
// load data into p
}
catch (System.Exception e)
{
Helper.SafeSendExceptionEmail("Integrator.Caching", e);
throw e;
}
MemoryCache.Default.Add(_cid, p, new CacheItemPolicy()
{
AbsoluteExpiration = DateTimeOffset.Now.AddHours(8),
RemovedCallback = arg =>
{
// query again to force reload
var d = data;
}
});
}
return (Product[])MemoryCache.Default[_cid];
}
}
}
}
Ok, by digging through MemoryCache
and MemoryCacheStore
source, it seems that cache is auto-disposed on domain unload at which point it disposes of all its stores, which in turn remove cache items with CacheSpecificEviction
reason. This reason is not used anywhere else so it must represent the "I'm dying" reason (they could have been more clear in the docs though)
public void Dispose()
{
if (Interlocked.Exchange(ref this._disposed, 1) == 0)
{
this._expires.EnableExpirationTimer(false);
ArrayList list = new ArrayList(this._entries.Count);
lock (this._entriesLock)
{
foreach (DictionaryEntry entry in this._entries)
{
MemoryCacheEntry entry2 = entry.Value as MemoryCacheEntry;
list.Add(entry2);
}
foreach (MemoryCacheEntry entry3 in list)
{
MemoryCacheKey key = entry3;
entry3.State = EntryState.RemovingFromCache;
this._entries.Remove(key);
}
}
foreach (MemoryCacheEntry entry4 in list)
{
this.RemoveFromCache(entry4, CacheEntryRemovedReason.CacheSpecificEviction, false);
}
this._insertBlock.Close();
}
}