Runtime: How to flush all cache?

Created on 20 Aug 2015  路  14Comments  路  Source: dotnet/runtime

area-Extensions-Caching feature request

Most helpful comment

+1 to adding a Clear() method to IMemoryCache and IDistributedCache.

I now expose a cache clearing method in my controllers as a matter of course. This stops you having to use the nuclear option of restarting the site to clear IMemoryCache or having to go off to Redis to clear IDistributedCache.

As it is, I will now have to keep a list of all my keys. Not a fun thing to do.

All 14 comments

Responding to dotnet/extensions#93 being related

I think and support there should be a cache clear option. Reasons could be

  1. Development and testing is made easier.
  2. With multiple Clients connected, keeping a local map of keys won't do the trick.
  3. Scanning over all the items to do this isn't a great idea
  4. All existing Distributed Caches support clear operations.

I was curious about the same thing. Further, from a diagnostics perspective I wanted to know everything that was in the cache (the argument of whether or I should or shouldn't iterate over cache keys aside). If I cast IMemoryCache that was injected as MemoryCache I was able to get the Count property but no keys exposed. Looking at the MemoryCache class there was just a Dictionary behind it like in past versions.

I forked the project at RC1, added a Keys property onto the MemoryCache class that returned the keys from the Dictionary. Built that, put the Nuget output into my custom Nuget feed and then updated my Nuget packages and let it do the rest.

+1 to adding a Clear() method to IMemoryCache and IDistributedCache.

I now expose a cache clearing method in my controllers as a matter of course. This stops you having to use the nuclear option of restarting the site to clear IMemoryCache or having to go off to Redis to clear IDistributedCache.

As it is, I will now have to keep a list of all my keys. Not a fun thing to do.

For those looking for a workaround, I have written and tested the following helper class with a ClearAsync, GetKeysAsync and a bulk RemoveAsync method which lets you remove multiple keys. It just needs to be added to the IServiceCollection DI container for use. Am willing to create a PR with these methods added if required.

public class DistributedCacheExtended : IDistributedCacheExtended, IDisposable
{
    private const string ClearCacheLuaScript =
        "for _,k in ipairs(redis.call('KEYS', ARGV[1])) do\n" +
        "    redis.call('DEL', k)\n" +
        "end";
    private const string GetKeysLuaScript = "return redis.call('keys', ARGV[1])";
    private readonly RedisCacheOptions options;
    private ConnectionMultiplexer connection;
    private IDatabase cache;
    private bool isDisposed;

    public DistributedCacheExtended(IOptions<RedisCacheOptions> redisCacheOptions)
    {
        this.options = redisCacheOptions.Value;
    }

    ~DistributedCacheExtended()
    {
        this.Dispose(false);
    }

    public async Task ClearAsync()
    {
        this.ThrowIfDisposed();
        await this.EnsureInitialized();
        await this.cache.ScriptEvaluateAsync(
            ClearCacheLuaScript,
            values: new RedisValue[]
            {
                this.options.InstanceName + "*"
            });
    }

    public async Task<IEnumerable<string>> GetKeysAsync()
    {
        this.ThrowIfDisposed();
        await this.EnsureInitialized();
        var result = await this.cache.ScriptEvaluateAsync(
            GetKeysLuaScript,
            values: new RedisValue[]
            {
                this.options.InstanceName + "*"
            });
        return ((RedisResult[])result).Select(x => x.ToString().Substring(this.options.InstanceName.Length)).ToArray();
    }

    public async Task RemoveAsync(string[] keys)
    {
        this.ThrowIfDisposed();
        if (keys == null) { throw new ArgumentNullException(nameof(keys)); }
        await this.EnsureInitialized();
        var keysArray = keys.Select(x => (RedisKey)(this.options.InstanceName + x)).ToArray();
        await this.cache.KeyDeleteAsync(keysArray);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected async Task EnsureInitialized()
    {
        if (connection == null)
        {
            this.connection = await ConnectionMultiplexer.ConnectAsync(this.options.Configuration);
            this.cache = this.connection.GetDatabase();
        }
    }

    private void Dispose(bool disposing)
    {
        if (!this.isDisposed)
        {
            if (disposing && this.connection != null)
            {
                this.connection.Close();
            }

            this.isDisposed = true;
        }
    }

    private void ThrowIfDisposed()
    {
        if (this.isDisposed)
        {
            throw new ObjectDisposedException(nameof(DistributedCacheExtended));
        }
    }
}

This is what I currently have to do with System.Runtime.Caching:

public void CacheBuster()
    {
        //Unfortunately Cache doesn't have a Clear method, so we have to do this the hard way.

        var memoryCache = Cache as MemoryCache;
        if (memoryCache != null)
            memoryCache.Trim(100); //this will clear most, but not all, of the cache.

        //This accounts for all cache types, not just MemoryCache.
        foreach (var item in Cache)
            Cache.Remove(item.Key);
    }

I really, really want to stop doing this.

So, @RehanSaeed what's the status on this matter? Do you have an example?

Sadly, nothings changed. Still waiting for this functionality.

I couldn't figure out the best area label to add to this issue. Please help me learn by adding exactly one area label.

The issue has been open for 5 years. Nothing changed. It's a very common request to clear all cache items in the memory.

Please can this issue be added as I am having immense issues flushing keys manually. Thank you

What would this look like as an API? (general format shown in https://github.com/dotnet/runtime/issues/new?assignees=&labels=api-suggestion&template=02_api_proposal.md&title=)

I've created a new issue with the proposal: https://github.com/dotnet/runtime/issues/45593

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yahorsi picture yahorsi  路  3Comments

matty-hall picture matty-hall  路  3Comments

noahfalk picture noahfalk  路  3Comments

jamesqo picture jamesqo  路  3Comments

omajid picture omajid  路  3Comments