We’ve started noticing something weird when using IsolatedCaches.
On startup we run this code via UmbracoApplicationStartedNotification:
var cache = appCache.IsolatedCaches.GetOrCreate<ItemStatus>();
cache.InsertCacheItem(CacheLoadedKey, () =>
{
return new ItemStatus()
{
IsLoaded = true
};
});
Then when doing an API call, we check that the item is loaded like this:
var cache = appCache.IsolatedCaches.GetOrCreate<ItemStatus>();
return cache.GetCacheItem<ItemStatus>(CacheLoadedKey)?.IsLoaded ?? false;
But. We’ve noticed that in our live environment. That out of nowhere the is starts returning false. Like the cache gets removed. This is the only code relating to the cache. We don’t do any .Clear().
We’ve checked the IIS and Windows server logs. But nothing. No error message. Nothing about the app pool being restarted. Also, nothing in the Umbraco logs. This happens really randomly. And we’re having difficulties seeing a pattern here.
I am not usually working with cache, so I might be handling the cache wrong here. It just feels really weird. Has anybody else experienced this?
This answer was largely generated by Claude, but is correct:
The most likely cause is memory pressure eviction. Umbraco’s IsolatedCaches uses System.Runtime.Caching.MemoryCache under the hood, which automatically evicts items when the system is under memory pressure; silently, with no errors or logs.
A few things to check:
Don’t treat the cache as reliable storage. Always code for the cache-miss case. Instead of checking once at startup, use a pattern that repopulates on demand:
var cache = appCache.IsolatedCaches.GetOrCreate<ItemStatus>();
var result = cache.GetCacheItem(CacheLoadedKey, () =>
{
// This factory runs when the item is missing
return LoadItemStatus();
});
This way, if the item gets evicted, it’s automatically recreated on next access.
Silent app pool recycles. IIS recycles app pools on a schedule (default: every 29 hours), on memory limits, and on other triggers. These don’t always show up in logs. Each recycle wipes all in-memory caches.
Cache priority. If you’re using InsertCacheItem without specifying a priority, the item is eligible for eviction. You won’t get a warning when this happens.
The fix is to never assume the cache is populated, always provide a fallback/factory to rebuild the data when it’s missing.
This sounds like application state, rather than a job for a memory cache. If you only need to do this once at Startup, and you’re persisting to memory indefinitely, then you’re better off just storing the value in a property/field in a service with a Singleton lifetime.
Thank you so much for your replies, Sebastiaan & Jason!
It just seems wild to me that it would silently evict items.
Yes, totally! We’ll look more into this! Thank you!
Is the eviction process activity-based? We’re using another like this that has 5000+ items:
var cache = appCache.IsolatedCaches.GetOrCreate<IEnumerable<Item>>();
cache.InsertCacheItem("item_1", () => new Item());
In this example. Would it evict items that haven’t been updated for a while or all items?
So let’s say item_532 hasn’t been updated for a while, but item_1 is updated regularly. When the eviction process kicks in. Would it only remove item_532. Or would it evict all 5000+ items?
Is it possible to set the cache priority using InsertCacheItem? How would I go about doing that?
Does the RuntimeCache also do a silent eviction of items? In that case, it feels like we’re going about this the wrong way. What’s generally the best practice for wanting to serve loads of items fast?
If you need that level of control over the memory cache you can skip Umbraco’s abstractions and inject an instance of IMemoryCache directly, or configure your own instance of a MemoryCache, and use that instead.