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] Ⓜ️ Microsoft HybridCache support #350

Closed
jodydonetti opened this issue Dec 22, 2024 · 3 comments
Closed

[FEATURE] Ⓜ️ Microsoft HybridCache support #350

jodydonetti opened this issue Dec 22, 2024 · 3 comments
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@jodydonetti
Copy link
Collaborator

jodydonetti commented Dec 22, 2024

Problem

Recently Microsoft introduced the new HybridCache in .NET 9.

This of course sparked a lot of questions about what I thought about it and the future of FusionCache.

So the "problem" seems to be a "fight" between FusionCache and HybridCache, right?

Wrong, imho.

Features

Looking at HybridCache from Microsoft, the main features are:

  • cache stampede protection
  • usable as L1 only (memory) or L1+L2 (memory + distributed)
  • multi-node notifications
  • tagging
  • serialization compression

All these features are there in FusionCache too (apart from compression, which I'm already working on).

FusionCache though has more, like:

  • fail-safe
  • soft/hard timeouts
  • adaptive caching
  • conditional refresh
  • eager refresh
  • auto-recovery
  • multiple named caches
  • advanced logging
  • events
  • background distributed operations
  • full OpenTelemetry support
  • all methods available as both sync+async (HybridCache will be async-only)

Solution

You can read my thoughts about it.

The main take away from my answer is this:

FusionCache will not leverage the new HybridCache, in the sense that it will not be built "on top of it", but it will do 2 main things:

  1. it may leverage some of the new bits being created for it
  2. will be available as an implementation of HybridCache

Long story short, Microsoft introduced not just a (default) implementation, but also an abstraction that anyone can implement: this may turn the HybridCache abstract class into some sort of "lingua franca" for a basic set of common features for all hybrid caches in .NET.

It means people will be able to do one of 2 things:

  • use FusionCache directly, as they did up until today
  • depend on a shared, Microsoft provided abstraction, but use a 3rd party implementation with maybe more features

To be clear, this does NOT mean that FusionCache will now be based on HybridCache from Microsoft, but that it will ALSO be available AS an implementation of it, via an adapter class.

Ok cool, but how?

Example

when setting up FusionCache in our Startup.cs file, we simply add .AsHybridCache():

services.AddFusionCache()
  .AsHybridCache(); // MAGIC

Now, every time we'll ask for HybridCache via DI (taken as-is from the official docs):

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
            cancellationToken: token
        );
    }

    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

we'll be using in reality FusionCache underneath acting as HybridCache, all transparently.

And this also means we'll have the power of FusionCache itself, including the resiliency of fail-safe, the speed of soft/hard timeouts and eager-refresh, the automatic synchronization of the backplane, the self-healing power of auto-recovery, the full observability thanks to native OpenTelemetry support and more.

Oh, and we'll still be able to get IFusionCache too all at the same time, so another SomeService2 in the same app, similarly as the above example, can do this:

public class SomeService2(IFusionCache cache)
{
    private IFusionCache _cache = cache;
    
    // ...

and the same FusionCache instance will be used for both, directly as well as via the HybridCache adapter.

Oh (x2), and we'll be even able to read and write from BOTH at the SAME time, fully protected from Cache Stampede!
Yup, this means that when doing hybridCache.GetOrCreateAsync("foo", ...) at the same time as fusionCache.GetOrSetAsync("foo", ...), they both will do only ONE database call, at all, among the 2 of them.

Oh (x3 😅), and since FusionCache supports both the sync and async programming model, this also means that Cache Stampede protection (and every other feature, of course) will work perfectly well even when calling at the same time:

  • hybridCache.GetOrCreateAsync("foo", ...) (async call from the HybridCache adapter)
  • fusionCache.GetOrSet("foo", ...) (sync call from FusionCache directly)

Damn if it feels good 😬

Of course, since the API surface area is more limited (eg: HybridCacheEntryOptions VS FusionCacheEntryOptions) we can enable and configure all of this goodness only at startup and not on a per-call basis: but still, it is a lot of power to have available for when you need/want to depend on the Microsoft abstraction.

Actually, to be more precise: the features available in both HybridCacheEntryOptions and FusionCacheEntryOptions (although with different names) have been automatically mapped and will work flawlessly: an example is using HybridCacheEntryFlags.DisableLocalCacheRead in the HybridCacheEntryOptions which becomes SkipMemoryCacheRead in FusionCacheEntryOptions, all automatically.

Separate package

As said, in preview-3 I introduced a new package for the adapter.

The idea was to put there the relavant code to avoid having to depend on extra stuff.

But, as some members of the community pointed out, the only dependency I need is with Microsoft.Extensions.Caching.Abstractions which is already a dependency FusionCache has.

Moral of the story, I'll move the relevant code in the main FusionCache package, empty the other one and deprecate it.

Microsoft (and Marc) and OSS

To me, this is a wonderful example of what it may look like when Microsoft and the OSS community have a constructive dialog. First and foremost many thanks to @mgravell himself for the openness, the back and forth and the time spent reading my mega-comments.

@jodydonetti jodydonetti self-assigned this Dec 22, 2024
@jodydonetti jodydonetti added the enhancement New feature or request label Dec 22, 2024
@jodydonetti jodydonetti added this to the v2.0.0 milestone Dec 22, 2024
@jodydonetti jodydonetti pinned this issue Dec 22, 2024
@mgravell
Copy link

When Microsoft (or rather: teams - people - inside Microsoft) discusses (discuss) whether to add functionality that overlaps some non-trivial portion of what existing OSS libraries offer, it is a socio-political minefield. Any random adding the same? Not a problem, but MSFT: were very aware of the perceived impact of the "behemoth" joining the party, and the impact of default choices.

As someone who has worked on many independent OSS libraries, I take this very seriously, and I take this note very positively. Thank you for taking the time to express this, and I look forward to continued successful interactions on this and other endeavours.

@jodydonetti
Copy link
Collaborator Author

For people missing my emoji-reaction: same here Marc, same here.

@jodydonetti
Copy link
Collaborator Author

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

@jodydonetti jodydonetti unpinned this issue Jan 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants