I have a .NET 6 application configured as a Windows Service where I am trying to make use of IMemoryCache
to store temporary access tokens used for API interactions. I am seeing some really odd behavior in the cache that is resulting in not being able to retrieve values that I have just added in a previous execution.
I have a simple abstraction for IMemoryCache
called ITokenCacheService
, which is implemented as such:
public class TokenCacheService : ITokenCacheService
{
private readonly IMemoryCache _cache;
public TokenCacheService(IMemoryCache cache)
{
_cache = cache;
}
public bool TryGetToken(TokenScope scope, out string token)
{
return _cache.TryGetValue(scope, out token);
}
public void AddTokenToCache(TokenScope scope, string token, int expiresIn)
{
_cache.Set(scope, token, TimeSpan.FromTicks(expiresIn));
}
}
The caller of this class is first attempting to TryGetToken()
to use the cached value, and if that fails, it reaches out to the API to get a new access token and then caches it with AddTokenToCache()
. This works well on the first pass, where the cache is expected to be empty and the token is therefore added. However, on the second pass when the background service runs again, the IMemoryCache
will fail to retrieve that same value that was just put in the cache.
Debugging this process, I can actually see my previously cached value in the cache before calling TryGetValue
. Even so, it returns false
and fails to return the cached value. Where it gets really strange - not only is it returning false
for a value that is actually cached, but after TryGetValue
executes, that value is then missing from the cache, even though it was there before the call. I modified TryGetToken
in the following way to test this:
public bool TryGetToken(TokenScope scope, out string token)
{
// Casting to have access to .Count
var cacheImpl = (MemoryCache)_cache;
var countBefore = cacheImpl.Count; // Count here is 1
var result = _cache.TryGetValue(scope, out token); // Result here is false
var countAfter = cacheImpl.Count; // Count here is 0 ???
return result;
}
I can get the token out if I try to retrieve it right after I set it. I tested this by modifying AddTokenToCache
just to check the value:
public void AddTokenToCache(TokenScope scope, string token, int expiresIn)
{
_cache.Set(scope, token, TimeSpan.FromTicks(expiresIn));
// Success here will be 'true' and 'test' will contain the token value.
var success = _cache.TryGetValue(scope, out var test);
}
The number of ticks being set for this token is 31536000, which amounts to 18 days and 6 hours. I don't think I'm hitting an expiration window. I am using the default registration method for the memory cache without any options:
services.AddMemoryCache();
Am I expecting this service to work in a way that it's not designed, or do I need to configure it some way that I'm not doing? Really not sure what I'm missing here. The value is clearly there in the cache when I come back around on the second pass, but TryGetValue()
is failing and seems to be removing it from the cache.
CodePudding user response:
The number of ticks being set for this token is 31536000, which amounts to 18 days and 6 hours
31536000 ticks is about 3.1 seconds, which is probably the cause of what you're seeing. MemoryCache
will only go looking for entries to evict when you interact with it. The fact that your entry is disappearing as soon as you try to query it is consistent with that entry expiring, and the MemoryCache
only noticing at that point.
18 days and 6 hours is a whopping 15768000000000 ticks.