dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.26k stars 4.73k forks source link

Enhancement to IDistributedCache - Allow for bulk remove and key enumeration #44512

Open maryamariyan opened 3 years ago

maryamariyan commented 3 years ago

As @eerhardt and I were triaging through caching extensions issues, we noticed a theme of requests being around allowing for key enumeration and bulk removal of items in IDistributedCache:

We closed a number of items as dupes of the above issues as well.

More information on the intricacies of this issue and ways to approach it:

ghost commented 3 years ago

Tagging subscribers to this area: @eerhardt, @maryamariyan See info in area-owners.md if you want to be subscribed.


Issue meta data

Issue content: As @eerhardt and I were triaging through caching extensions issues, we noticed a theme of requests being around request to allow for key enumeration and bulk removal of items in IDistributedCache: - [ ] #36547: How to flush all cache - [ ] #36568: Bulk remove on IDistributedCach - [ ] #36402: Enumerating keys using IDistributedCache We closed a number of items as dupes of the above issues as well. More information on the intricacies of this issue and ways to approach it: - When using caching in extensions, we don't want to allow enumerating on keys while elements may get removed within another thread, (keeping as atomic operation). In order to allow for (e.g. bulk remove) we can offer returning a snapshot (say array of elements. For example to allow for bulk remove of items with a certain prefix (e.g. "ZZZ"), given a snapshot, we may use TryGetValue and if already gone it would not throw.
Issue author: maryamariyan
Assignees: -
Milestone: -

udlose commented 3 years ago
  • ..... For example to allow for bulk remove of items with a certain prefix (e.g. "ZZZ"), given a snapshot, we may use TryGetValue and if already gone it would not throw.

@maryamariyan, StackExchange.Redis.IDatabase.KeyDeleteAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None) does not throw an exception if a key is not found. See - https://github.com/StackExchange/StackExchange.Redis/blob/6409b9d1f2afd01a7eab9498a525360d1ca2b752/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs#L442

Is there a concern I'm missing?

kosmakoff commented 3 years ago

I wonder if it is being worked on. I recently stumbled upon a case when I very much need to enumerate values (or keys at least) by pattern (prefix), otherwise I will have to use underlying caching solution directly, which breaks my abstractions.

udlose commented 3 years ago

It looks like this has been added to the .NET 6.0 milestone (tentatively sched to be released in November 2021 - https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-2/#:~:text=.NET%206%20will%20be%20released,Term%20Support%20(LTS)%20release.

eerhardt commented 3 years ago

We've reached feature complete for .NET 6. Moving to 7.

avisra commented 2 years ago

So this is being bumped again to .NET 8 now?

maryamariyan commented 2 years ago

@avisra this is still being considered for 7.0 as a nice to have but not committed

maryamariyan commented 2 years ago

@avisra I was looking to learn more about how IDistrbutedCache is being used? do you have sample code snippets to share to kick this off?

avisra commented 2 years ago

@maryamariyan I am currently using IDistributedCache for caching data in my application (and for caching things within my IdentityServer instance). On my deployed application, I am configuring my cache to use Redis. Locally, I am using in-memory. But regardless of the provider, it is going through IDistributedCache. My goal for this ticket is to be able to enumerate the cache without identifying the provider. I'd like to have a page in the administration of my application which displays a table with all of the objects in my cache (by key) and have a button for deleting/revalidating, etc. The only piece missing from this, which I'm hoping this ticket can provider, is the ability to enumerate the objects in the cache collection.

Strandedpirate commented 2 years ago

@avisra Here are a couple of potential interface additions for IDistributedCache for getting and performing common tasks with keys.

// retrieve a list of keys list synchronously using a pattern (pattern will be third-party dependent)
IEnumerable<string> Keys(string pattern);
// retrieve a list of keys asynchronously using a pattern (pattern will be third-party dependent)
Task<IEnumerable<string>> KeysAsync(string pattern);

// retrieve a list of values synchronously based on a set of keys
IEnumerable<byte[]> Get(param string[] keys);
// retrieve a list of values asynchronously based on a set of keys
Task<IEnumerable<byte[]>> GetAsync(param string[] keys);

// remove a list of values synchronously based on a set of keys
void Remove(param string[] keys);
// remove a list of values asynchronously based on a set of keys
Task RemoveAsync(param string[] keys);

// refresh a list of values synchronously based on a set of keys
void Refresh(param string[] keys);
// refresh a list of values asynchronously based on a set of keys
Task RefreshAsync(param string[] keys);

usage:

// assume that the developer will have a wrapper that converts from bytes (used in `IDistrubutedCache` interface) to a .net value type or reference type.
// redis specific example. using star (*) as the wildcard
var allAccessTokenKeys = _redisCacheService.Keys("AccessTokens:*");
foreach(var key in allAccessTokenKeys)
{
    Console.WriteLine(key);
}

// get all the values for all the keys we retrieved above
var allAccessTokens = _redisCacheService.Get(allAccessTokenKeys.ToArray());
foreach(var token in allAccessTokens)
{
    Console.WriteLine(token);
}

// refresh all allAccessTokens in the cache, so they don't expire
_redisCacheService.Refresh(allAccessTokenKeys.ToArray());

// remove all allAccessTokens from the cache, except a special one
_redisCacheService.Remove(allAccessTokenKeys.Where(a => a.Equals("AccessTokens:MySpecialKey") == false).ToArray());
wilkovanderveen commented 1 year ago

It seems that this is already possible with libraries such as EasyCaching.