Home > Software design >  IMemoryCache Returning False for Cached Value
IMemoryCache Returning False for Cached Value

Time:02-25

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.

  • Related