Responding to dotnet/extensions#93 being related
I think and support there should be a cache clear option. Reasons could be
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
Most helpful comment
+1 to adding a
Clear()
method toIMemoryCache
andIDistributedCache
.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 clearIDistributedCache
.As it is, I will now have to keep a list of all my keys. Not a fun thing to do.