Clean / Dispose Expired Cache entries

Hi,

I have some services that serve data that is valid only for a limited period of time, is cached for a “short” time and after a relative small period will not be requested. This service memory grows until eat all available memory and stop working.

public class CachedServiceBlahBlah : Service  {
    [CacheResponse(Duration = 3, LocalCache = true)]
    public object Get(GetEvent request) { ... }
}
[Route("/event/{uid}", verbs: "GET"]
public class GetEvent{
    public string Uid { get; set; }
    public string Var1 { get; set; }
    public string Var2 { get; set; }
}

As i see on source (CacheResponseAttribute), is cached with a key composed with the RawUrl, and as is a GET request, the url will be something like “/event/123456789A?Var1=AAAAA&Var2=BBBBB”, correct?.

After the cache expires, the cache entry will remain into memory, and never removed right?

Have I missed something?
Can I configure MemoryCacheClient to flush/remove cache entries when they expire?
Should I code my own MemoryCacheClient class with “auto flush/remove” expired entries?

RTM ENV: Windows, Windows Service, .Net Framework 4.6.2, ServiceStack 5.5

When a expired cache is accessed the MemoryCacheClient should remove the cache entry, if a new cache is created it should override the existing entry. Maybe your events (and cache entries) are all unique and the caches aren’t getting hit? Which doesn’t make it a good candidate for caching.

There’s also an explicit RemoveExpiredEntries() you can resolve from IRequest if needed, e.g:

((MemoryCacheClient) base.Request.GetMemoryCacheClient()).RemoveExpiredEntries();

The events have a short life (90…180 minutes) and change their status every few seconds.
Once dead, they will never be requested again.

Per request may be little “abuse” because it have a lot of requests and already consuming 100% CPU (searching cache items into MemoryCacheClient.memory)

For now, have implemented a MemoryCacheClient class with removing expired every 30 minutes. If necessary I will improve the implementation.

public class AutoRemoveExpiredEntriesMemoryCacheClient : MemoryCacheClient
{
    private readonly Timer _removeTimer;
    private TimeSpan _removePeriod = TimeSpan.FromMinutes(30);

    public AutoRemoveExpiredEntriesMemoryCacheClient() : base()
    {
        _removeTimer = new Timer((_) => RemoveExpiredEntries(), null, _removePeriod, _removePeriod);
    }

    public bool AutoRemoveActive => _removePeriod == TimeSpan.Zero;

    public TimeSpan RemovePeriod
    {
        get
        {
            return _removePeriod;
        }

        set
        {
            if (value <= TimeSpan.Zero)
            {
                _removePeriod = TimeSpan.Zero;
                _removeTimer.Change(-1, -1);
            }
            else
            {
                _removePeriod = value;
                _removeTimer.Change(_removePeriod, _removePeriod);
            }
        }
    }
}
1 Like

If you’re going to use a Timer I wouldn’t subclass MemoryCacheClient for this, I’d just start the timer in your AppHost and have it call

Container.TryResolve<MemoryCacheClient>().RemoveExpiredEntries();

Or you can remove needing to spawn a background Timer thread with a Global Request Filter that periodically calls the API every n requests, e.g:

private long counter;
public override void Configure(Funq.Container container)
{
    GlobalRequestFilters.Add((req, res, dto) => {
        if (Interlocked.Increment(ref counter) % 1000 == 0)
        {
          ((MemoryCacheClient)req.GetMemoryCacheClient()).RemoveExpiredEntries();
        }
    });
}