ASP.NET Core HybridCache library

Almost all real-world applications need to implement a caching mechanism. Caching can drastically improve application performance, scalability, and user experience while optimizing resource use and reducing costs. .NET supports both in-memory and distributed cache implementation and is easily configured via middleware. Additionally, the application code interacts with the IDistributedCache interface and remains completely abstracted away from the cache implementation detail. For most applications, Redis remains the de facto Level 2 cache, and it works extremely well.

However, recently I worked on an ecommerce project where this default approach did not quite work. When the user scrolled through a list of items (particularly on a mobile device where they can swipe/scroll fast) caused intermittent jitters. After countless hours of testing and troubleshooting we realized that it was due to network hops to the Redis cluster when retrieving items from the cache. This presented us with an interesting technical challenge and required a bit more work to get to the buttery smooth user experience that we were shooting for. Essentially, we needed the speed of in-memory cache with the data consistency of a distributed cache. After careful analysis and weighing the pros and cons of all possible solutions we decided to use in-memory cache to avoid the network hops all together and implement our own cache synchronization mechanism to enforce data consistency amongst our cluster of app instances. For us, and for this particular use case, the benefits outweigh the drawbacks. The solution, as it turns out, still needed a central redis cluster but we only used Redis’s Pub/Sub capability to provide the communication machinery between the app instances. Essentially, each application would register themselves as both a publisher and a subscriber. During a cache eviction, the particular app instance that received the request (acting as a publisher) would notify others of the eviction. All subscribers would self evict that particular item from the cache. This worked surprisingly well and allowed us to get to the smooth user experience we were hoping for. However, the extra plumbing code that we added to orchestrate this intricate dance felt icky and a possible hotspot for future bugs. 

.NET 9 HybridCache solves this exact problem and it even does it better. It combines in-memory caching and distributed caching for you out-of-the box without the need to add custom code. The registration and configuration process remains the same view middleware. It even provides additional built in features, such as: 

Tag based cache eviction

Suppose, in addition to caching a single product by its id, we store products in different caches based on category. It is common for a single product to belong to multiple categories (for e.g., a shirt could be in the following categories: Dress Shirts, T-Shirts, Tops). With the tag support provided in HybridCache we have the ability to update all our caches using a single command.

Configurable serialization

By default, it uses System.Text.Json for serialization. But we are able to add custom serialization (such as Protobuf) during the registration process.

One small difference is that we lose the abstraction provided by the IDistributedCache interface and now must rely explicitly on the injected HybridCache instance. However, the default behavior for both IDistributedCache and HybridCache remains the same, which is to use in-memory caching if a Level 2 cache is not configured. 

Sample github project link

builder.Services.AddHybridCache(options =>
{
	options.MaximumPayloadBytes = 1024 * 1024;
	options.MaximumKeyLength = 1024;
	options.DefaultEntryOptions = new HybridCacheEntryOptions
	{
		Expiration = TimeSpan.FromMinutes(defaultCacheTime),
		LocalCacheExpiration = TimeSpan.FromMinutes(defaultCacheTime)
	};
});