Using CacheManager, how would you best go about implementing this scenario, where the cache instance contains data, that can take a long time to get from a slow source?
I never want the users to wait for the cache to populate (I am not concerned about first load)
I can think of two ways, but not sure if they are possible with CacheManager:
What is technically possible with Cachemanager, and which approach works best - if at all?
Regarding your second option, "Refresh on expiry", this doesn't work because CacheManager doesn't return the cached value if the item expired. And, while you "refresh" the cache, you'd eventually get a lot of cache misses from other callers.
My suggestions:
If you have only a small amount of keys cached for 60 minutes and they all "work the same", just spin up a background task which runs async to your process and refreshes the cache every 15 minutes.
If you have many keys which can vary a lot in terms of expiration, you could cache the data with 60 minutes expiration and store a secondary key (a fake key with no actual value) with a 15 minutes expiration. Then, if the fake key expires, refresh the actual key/value...
As key
for the fake one, use a prefix+actual key for example and then listen to OnRemove
event for example.
Quick example program
internal class Program
{
private static ICacheManager<object> _cache;
private static void Main(string[] args)
{
_cache = CacheFactory.Build(c => c.WithDictionaryHandle());
_cache.OnRemoveByHandle += Cache_OnRemoveByHandle;
for (var i = 0; i < 10; i++)
{
_cache.Add("key" + i, "data" + i);
AddFakeKey("key" + i);
Thread.Sleep(1000);
}
Console.ReadKey();
}
private static void AddFakeKey(string forKey)
{
_cache.Add(new CacheItem<object>("trigger_" + forKey, "n/a", ExpirationMode.Absolute, TimeSpan.FromSeconds(1)));
}
private static void Cache_OnRemoveByHandle(object sender, CacheItemRemovedEventArgs e)
{
// Remark: you might get this event triggered for each level of the cache e.Level can be checked to react only on the lowest level...
Console.WriteLine("OnRemoveByHandle " + e.ToString());
if (e.Key.StartsWith("trigger_") && e.Reason == CacheItemRemovedReason.Expired)
{
var key = e.Key.Substring(8);
Console.WriteLine("Updating key " + key);
// updating the key
_cache.Update(key, _ => "new value");
// add a new fake key for another round
AddFakeKey(key);
}
}
}
This even works with Redis if keyspace notifications are enabled.