HybridCache

Cache Library.NETC#MicrosoftMemory CacheDistributed CachePerformance

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.

Stars37,296
Watchers1,431
Forks10,465
Created:March 11, 2014
Language:C#
License:MIT License

Topics

aspnetcoredotnethacktoberfesthelp-wanted

Star History

dotnet/aspnetcore Star History
Data as of: 10/22/2025, 10:05 AM

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

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);
        }
    }
}