Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Group cache invalidation (cache regions) #294

Closed
codingaddicted opened this issue Aug 29, 2024 · 8 comments
Closed

[FEATURE] Group cache invalidation (cache regions) #294

codingaddicted opened this issue Aug 29, 2024 · 8 comments

Comments

@codingaddicted
Copy link

Problem

When you have a cache containing items grouped in some way (within the same cache instance), you may have the need to remove/invalidate them as a set and not by every single item key (you also may not have all their keys).
For example, if you are using cache for many elements within a container and you want to invalidate all of them in a single operation knowing the container identifier only.

In this cases you probably compose the cache key using a common prefix (the group id or something similar), but the supported invalidation/removal methods only work on specific key and they don't support a "starts with" logic for example.

Solution

Some other cache libraries support the concept of "region". When you set a cache entry, you can do it using the key only or a key/region pair. In this way all invalidation procedures can be easily done using the single key (obviously) and also using the region identifier, to remove all associated items.

Alternatives

An alternative solution (easier to achive, but more flexible for some aspects) solution could be supporting a startsWith/endsWith/contains (or even RegEx) logic on cache key on invalidation operations. In this way you can use prefixes to identify more then one item.

Example

Both solutions stand on an abstraction to handle region items keys, which need to be saved internally on every provider.
For example, in MemoryCacheAccessor, the SetEntry method should manage something like this:

var (memoryEntryOptions, absoluteExpiration) = options.ToMemoryCacheEntryOptionsOrAbsoluteExpiration(_events, _options, _logger, operationId, key);

if (memoryEntryOptions is not null)
{
	entry.PhysicalExpiration = memoryEntryOptions.AbsoluteExpiration!.Value;

	_cache.Set<IFusionCacheMemoryEntry>(key, entry, memoryEntryOptions);
	if (!string.IsNullOrEmpty(region))
	{
		// this extension method manages a dictionary regionKey/itemKeys within the same cache
		_cache.SetRegionChild(item.Region, key);
	}
}
else if (absoluteExpiration is not null)
{
	entry.PhysicalExpiration = absoluteExpiration.Value;

	_cache.Set<IFusionCacheMemoryEntry>(key, entry, absoluteExpiration.Value);
}
else
{
	throw new InvalidOperationException("No MemoryCacheEntryOptions or AbsoluteExpiration was determined: this should not be possible, WTH!?");
}

On the other way, the RemoveEntry should be

public void RemoveEntry(string operationId, string key, string region, FusionCacheEntryOptions options)
{
	// ACTIVITY
	using var activity = Activities.SourceMemoryLevel.StartActivityWithCommonTags(Activities.Names.MemoryRemove, _options.CacheName, _options.InstanceId!, key, operationId, CacheLevelKind.Memory);

	if (_logger?.IsEnabled(LogLevel.Debug) ?? false)
		_logger.Log(LogLevel.Debug, "FUSION [N={CacheName} I={CacheInstanceId}] (O={CacheOperationId} K={CacheKey}): [MC] removing data (from memory)", _options.CacheName, _options.InstanceId, operationId, key);

	_cache.Remove(key);
	if (!string.IsNullOrEmpty(region))
	{
		_cache.RemoveRegionChild(key)
	}

	// EVENT
	_events.OnRemove(operationId, key);
}
@jodydonetti
Copy link
Collaborator

Hi @codingaddicted , sorry for the delay.

I'm working on adding tagging support, which would be even more powerful than regions.

Will update as soon as there's something more concrete.

@codingaddicted
Copy link
Author

Hi @jodydonetti, thank you for your feedback.

I agree with you that tags would be even a more flexible solution than a single region, as they would allow you to link a single cache item to multiple "groups".
Tag support is also an opened issue on the other caching library I'm currently using, but they have no plans to implement the feature.

This would be a turnkey point for your project. I’ll keep a close eye on how this evolves with great interest.

@dario-l
Copy link

dario-l commented Sep 27, 2024

Hi @codingaddicted , sorry for the delay.

I'm working on adding tagging support, which would be even more powerful than regions.

Will update as soon as there's something more concrete.

Oh... that would be uber-super feature. We are developing a new version of our product. For now we use some in-house implementation of our own IHybridCache but in our implementation is lack of eviction by tags.

Our code with use-cases suffer and we need this badly. :)

We will look forward for your continuation on this. 😸

@jodydonetti
Copy link
Collaborator

Oh... that would be uber-super feature

Eh, I know but it's uber-super complex to do with the limited IDistributedCache as a supporting interface 😅

Now I'm on vacation, but when I come back I'll create an issue with the details there.

@dario-l
Copy link

dario-l commented Oct 14, 2024

Eh, I know but it's uber-super complex to do with the limited IDistributedCache as a supporting interface 😅

@jodydonetti maybe event not possible...

How is going? 😄

Checkout RedisOutputCacheStore.EvictByTagAsync implementation 👍🏻

@jodydonetti
Copy link
Collaborator

UPDATE: it is happening 🥳

Any help would be appreciated!

@jodydonetti
Copy link
Collaborator

Hi all, v2.0.0-preview-1 is out 🥳
This includes Tagging and Clear() support!

🙏 Please, if you can try it out and let me know what you think, how it feels to use it or anything else really: your contribution is essential, thanks!

@jodydonetti
Copy link
Collaborator

Hi all, I still can't believe it but v2.0.0 is finally out 🎉
Now I can rest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants