Using FusionCache's Backplane to synchronize HybridCache instances across multiple instances
The decision of whether to use a cache or not depends entirely on your application's needs, but if you do require caching, then .NET provides several options to choose from.
Caching in ASP.NET link
Previously, you could choose between using an in-memory cache (IMemoryCache) or a distributed cache (IDistributedCache) based on your requirements. The recent addition of the HybridCache - in .NET 9 - combines the best of both worlds, and provides a simple and consistent API for developers.
The HybridCache works by first checking the in-memory cache (L1) for the requested item. If it's not found there, it then checks the distributed cache (L2). If the item is found in the distributed cache, it is then added to the in-memory cache for faster access when it's read another time. This approach ensures that frequently accessed data is quickly available while still maintaining consistency across different instances of your application.
Next to the speed improvements and the DX, the HybridCache also has the following features:
- Cache-stampede protection: Prevents multiple requests from overwhelming the cache when an item expires.
- Cache invalidation: use tags to invalidate related cache entries across both caches.
- Configurable: define where you want to cache (L1 and/or L2) your data and how it should be serialized.
Using HybridCache link
Installation and setup link
To start using the HybridCache, you need to install the Microsoft.Extensions.Caching.Hybrid package.
Next, register the HybridCache using the AddHybridCache() method in your the Program.cs file:
The above registers the HybridCache as an in-memory cache.
When a distributed cache implementation (like Redis or SQL Server) is in the dependency injection container, it will automatically be used as the distributed cache.
For example, to use Redis as the distributed cache, you can register it like this:
AddHybridCache() can also take a callback action to customize the behavior of the cache by providing HybridCacheOptions. For example, to disable the local cache and set a custom expiration time for cache entries, you can do the following.
Instead of configuring the expiration time for each cache entry individually, it's useful to set default options for your distributed cache entries using these options.
Retrieving and writing from/to the HybridCache link
To consume the HybridCache, inject a HybridCache instance and use the GetOrCreateAsync method to retrieve or create cache entries.
This methods requires a cache key and a factory function to create the item if it doesn't exist in the cache.
GetOrCreateAsync also accepts optional options argument (HybridCacheEntryOptions) to customize the caching behavior for that specific entry, such as setting expiration times, and an optional tags argument to associate tags with the cache entry.
Besides the GetOrCreateAsync method, the HybridCache also provides methods to remove cache entries (RemoveAsync and RemoveByTagAsync), as well as a method to set cache entries directly (SetAsync).
For those looking to migrate, it should be relatively straightforward to migrate to the HybridCache as it is a drop-in replacement for both IMemoryCache and IDistributedCache, especially if you are already using the memory cache because it has a similar API.
Problem: Keeping multiple in-memory caches in sync link
But this also raises the question, how do you keep the cache in sync for different in-memory instances, for example when you have multiple instances of the same application running?
Sadly, the HybridCache does not provide a built-in solution for this scenario yet. There's an open issue on GitHub discussing this exact problem.
One possible approach to tackle this problem is to use the Redis Pub/Sub messaging system to handle cache invalidation across different instances.
FusionCache link
Another approach is to introduce the FusionCache package, which is a third-party caching library that provides advanced features to have a resilient and flexible caching solution. It supports more advanced features compared to HybridCache, such as Backplane, which can be used to synchronize cache entries across multiple instances using a distributed messaging system. For a full comparison between different caching libraries, check out the comparison table.
We're not going into all of FusionCache's features in this post, as that could be a blog post on its own. If you want to know more about it, check out the documentation, which is very well written and provides many examples.
Luckily, FusionCache also provides a HybridCache wrapper so you don't have to change your existing codebase if there's already a HybridCache being used.
Setup link
To install the core library of FusionCache as a distributed cache install the following packages:
To register FusionCache in your application, use the AddFusionCache() method, which sets up FusionCache as an in-memory cache.
To configure FusionCache to use a distributed cache (like Redis), you can use the WithDistributedCache() method, which requires you to also configure the serializer using the WithSerializer() method.
Because we want it to act as a HybridCache wrapper, use the AddFusionHybridCache() method.
The rest of the code remains the same, as you can keep using the HybridCache API.
Backplane support link
To add backplane support, you can use the WithRedisBackplane() method when configuring FusionCache.
With this setup, whenever a cache entry is removed or updated in one instance, a message is published, which notifies all other instances to invalidate their local caches accordingly. This ensures that all instances have consistent cache data.
Conclusion link
If you're considering using caching in your .NET applications, the HybridCache is a great choice due to its simplicity and built-in features. You can get started quickly with minimal setup and enjoy the benefits of in-memory caching, while still having a distributed cache as an option for larger-scale deployments. Changing the caching mechanism can be done in a central place, without affecting the rest of your application code.
The two-tier (in-memory and distributed) caching approach of HybridCache provides a good balance between performance and scalability.
However, it might not offer all the features that you need. This is where FusionCache comes in.
Because FusionCache provides a wrapper for HybridCache, it should be easy to integrate it into your existing applications and migrate to it.
In this post we've seen how to use FusionCache's backplane feature to keep multiple in-memory caches synchronized across different instances of your application. The best part is that this requires minimal code changes—just a few extra configuration lines during setup.
If you're running a distributed application with multiple instances and need consistent cache data across all of them, combining the HybridCache API with FusionCache is a practical approach that doesn't require a complete rewrite of your caching logic. As an added bonus, you'll also gain access to FusionCache's other resilience features should you need them in the future.
Feel free to update this blog post on GitHub, thanks in advance!
Join My Newsletter (WIP)
Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.
Support me
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.