HybridCache
GitHub Overview
dotnet/aspnetcore
ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
Topics
Star History
Cache Library
HybridCache
Overview
HybridCache is a new caching library introduced in .NET 9 by Microsoft, providing a unified solution that combines in-memory caching (L1) and distributed caching (L2).
Details
HybridCache is a next-generation caching library designed to address the limitations of traditional IMemoryCache and IDistributedCache. It uses fast in-memory caching as L1 cache and automatically leverages distributed caches like Redis or SQL Server as L2 cache. To prevent cache stampede problems, when multiple concurrent requests for the same data occur, only the first request executes the operation while other requests wait for its result. It provides tag-based invalidation functionality for efficiently removing related cache entries. The library supports a wide range of runtimes from .NET Framework 4.7.2 to .NET 9 and is designed as a drop-in replacement for traditional caching APIs. It offers automatic object serialization, custom serializer support, and configurable expiration options.
Pros and Cons
Pros
- Unified API: Single API for both in-memory and distributed caching
- Cache Stampede Prevention: Efficiently handles multiple requests for the same data
- L1/L2 Tier Architecture: Optimal combination of fast memory cache and large distributed cache
- Tag-based Invalidation: Bulk removal of related cache entries
- Automatic Serialization: Object conversion and custom serializer support
- Broad Compatibility: Supports .NET Framework 4.7.2 to latest .NET
- Configurable: Rich configuration options and flexible expiration management
Cons
- New Library: Requires .NET 9 (backward compatibility available but with limitations)
- Learning Curve: Need to learn new APIs and concepts
- Complexity: L1/L2 cache management can be complex in some cases
- Dependencies: Requires Microsoft.Extensions.Caching.Hybrid package
- Configuration Required: Proper configuration needed for optimal performance
Official Links
- HybridCache Official Documentation
- Microsoft.Extensions.Caching.Hybrid NuGet
- ASP.NET Core Official Blog
- GitHub HybridCache Samples
- Microsoft Learn Tutorial
Code Examples
Basic Setup and DI Registration
// Program.cs
using Microsoft.Extensions.Caching.Hybrid;
var builder = WebApplication.CreateBuilder(args);
// Register HybridCache
builder.Services.AddHybridCache();
// Use Redis as distributed cache
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
var app = builder.Build();
Basic Cache Operations
public class ProductService
{
private readonly HybridCache _cache;
public ProductService(HybridCache cache)
{
_cache = cache;
}
public async Task<Product> GetProductAsync(int id, CancellationToken cancellationToken = default)
{
return await _cache.GetOrCreateAsync(
$"product:{id}", // Cache key
async cancel => await FetchProductFromDatabaseAsync(id, cancel), // Factory method
token: cancellationToken
);
}
}
Expiration and Options Configuration
public class UserService
{
private readonly HybridCache _cache;
public UserService(HybridCache cache)
{
_cache = cache;
}
public async Task<List<User>> GetUsersAsync(string category)
{
var options = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(30), // Shared expiration for L1 and L2
LocalCacheExpiration = TimeSpan.FromMinutes(5) // L1-only expiration
};
return await _cache.GetOrCreateAsync(
$"users:category:{category}",
async cancel => await GetUsersFromDatabaseAsync(category, cancel),
options,
token: CancellationToken.None
);
}
}
Advanced Configuration and Customization
// Startup/Program.cs
builder.Services.AddHybridCache(options =>
{
// Maximum payload size (10MB)
options.MaximumPayloadBytes = 1024 * 1024 * 10;
// Maximum key length
options.MaximumKeyLength = 512;
// Default expiration settings
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(30),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
Custom Serializer Implementation
public class CustomProductSerializer : IHybridCacheSerializer<Product>
{
public Product Deserialize(ReadOnlySequence<byte> source)
{
// Custom deserialization logic
return JsonSerializer.Deserialize<Product>(source);
}
public void Serialize(Product value, IBufferWriter<byte> target)
{
// Custom serialization logic
JsonSerializer.Serialize(target, value);
}
}
// Registration
builder.Services.AddHybridCache()
.AddSerializer<Product, CustomProductSerializer>();
Tag-based Invalidation
public class CacheInvalidationService
{
private readonly HybridCache _cache;
public CacheInvalidationService(HybridCache cache)
{
_cache = cache;
}
public async Task InvalidateProductCacheAsync(int categoryId)
{
// Invalidate all cache entries with specific tag
await _cache.RemoveByTagAsync($"category:{categoryId}", CancellationToken.None);
}
public async Task SetProductWithTagsAsync(Product product)
{
var options = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(30)
};
// Store in cache with tags
await _cache.SetAsync(
$"product:{product.Id}",
product,
options,
new[] { $"category:{product.CategoryId}", "products" },
CancellationToken.None
);
}
}
Error Handling and Logging
public class RobustCacheService
{
private readonly HybridCache _cache;
private readonly ILogger<RobustCacheService> _logger;
public RobustCacheService(HybridCache cache, ILogger<RobustCacheService> logger)
{
_cache = cache;
_logger = logger;
}
public async Task<T> GetOrCreateWithFallbackAsync<T>(
string key,
Func<CancellationToken, Task<T>> factory,
CancellationToken cancellationToken = default)
{
try
{
return await _cache.GetOrCreateAsync(key, factory, token: cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Cache error occurred. Key: {Key}", key);
// Execute factory method directly on cache failure
return await factory(cancellationToken);
}
}
}