Skip to content

Two-Tier Cache implementation for .NET based on Microsoft.Extensions.Caching

License

Notifications You must be signed in to change notification settings

fdipuma/twotiercache

Repository files navigation

Two-Tier Cache

Build Status Azure DevOps tests Azure DevOps coverage

Overview

TwoTierCache is an super-simple open source caching implementation for .NET that supports a two-tier strategy for setting and retrieving cached values.

A typical scenario in modern CPUs is to have multiple layer of caching (e.g. L1, L2, L3, RAM, Disk), each with different performance and characteristics.

A two-tier strategy means to give the user the ability to store cached values into a first (fast but volatile) layer and also in a second (slower, persisted and distributed) layer, so different instances of the same application can speed-up the cache retrieving process by looking sequentially on the layers.

This project was inspired by this post by StackOverflow on how they manage cache (spoiler: in memory + redis).

Packages

Name NuGet Package Description
TwoTierCache.Abstractions Nuget Interfaces and other abstractions used by the other packages
TwoTierCache Nuget Main implementation of a default ITwoTierCache that uses Microsoft.Extensions.Caching primitives for IDistributedCache
TwoTierCache.EvictionSignaling.Redis Nuget Backplane service that uses Redis for syncing in memory evictions from multiple instances (using Redis pub/sub)
TwoTierCache.AspNetCore.TicketStore Nuget Custom ITicketStore for ASP.NET Core to be used as Session Store of AuthenticationTicket in case of Cookies Auth

Quick start

ASP.NET Core App

Simple scenario that uses Microsoft.Extensions.Caching.MemoryCache as the first layer and Redis as the second layer inside an AspNetCore app.

  1. First install nuget packages
dotnet add package TwoTierCache
dotnet add package TwoTierCache.EvictionSignaling.Redis
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
  1. Add services to service collection (Startup.cs or Program.cs in minimal APIs):
// first register the two-tiers (in memory + distributed)
services.AddMemoryCache();
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = _config["MyRedisConStr"];
    options.InstanceName = "SampleInstance";
});

// then add two tier cache
services.AddTwoTierCache();

// add eviction signaler so other instances will never serve stale data
services.AddRedisCacheEvictionSignaler(options =>
{
    options.Configuration = _config["MyRedisConStr"];
    options.EvictionChannelName = "MyCustomChannel"; // used to customize which Redis pub/sub channel to use
});
  1. Use the cache:
public class SampleController : Controller
{
    private readonly ITodoRepository _repository;
    private readonly ITwoTierCache _cache;

    // inject ITwoTierCache into Controllers or Services
    public SampleController(ITodoRepository repository, ITwoTierCache cache)
    {
        _repository = repository;
        _cache = cache;
    }
    
    [HttpGet("{id}")]
    public async Task<ToDoItem> Get(int id, CancellationToken token)
    {
        var cacheKey = $"todo-{id}";
        
        return await _cache.GetOrCreateAsync<ToDoItem>(cacheKey, async entryOptions =>
        {
            entryOptions.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(5); //  handle cache expiration
            return await _repository.GetItemAsync(id);
        }, token);
    }
    
    [HttpPut("{id}")]
    public async Task<ToDoItem> Put(int id, ToDoItem item, CancellationToken token)
    {
        await _repository.UpdateAsync(id, item, token);
        
        // remove item from the cache so we do not serve stale data
        await _cache.RemoveAsync($"todo-{id}", token);
    }    
}

Cookies Authentication SessionStore

In ASP.NET Core, Cookies Authentication stores all auth session information (e.g. claims) into a cookie.

This makes the entire interaction stateless (which is good) but also allows this cookie to grow indefinitely each time a new claim is added to the user identity.

Another option is to use a custom SessionStore where to store AuthenticationTicket, so the Cookies Authentication handler sends a cookie with just a reference to the storage instead of serializing the entire user identity.

TwoTierCache.AspNetCore.TicketStore allows you to use a Two-Tier cache as session storage for authentication tickets, so you can store tickets both in memory and on distributed cache while keeping in sync instances with an evictio signaler.

  1. Install packages
dotnet add package TwoTierCache
dotnet add package TwoTierCache.EvictionSignaling.Redis
dotnet add package TwoTierCache.AspNetCore.TicketStore
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
  1. Configure services
// register all services as seen in the ASP.NET Core example above
services.AddMemoryCache();
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = _config["MyRedisConStr"];
    options.InstanceName = "SampleInstance";
});

// then add two tier cache
services.AddTwoTierCache();

// add eviction signaler so other instances will never serve stale data
services.AddRedisCacheEvictionSignaler(options =>
{
    options.Configuration = _config["MyRedisConStr"];
    options.EvictionChannelName = "MyCustomChannel"; // used to customize which Redis pub/sub channel to use
});

// cookies auth configuration
services.AddAuthentication("cookies")
    .AddCookie("cookies", o =>
    {
        o.Cookie.HttpOnly = true;
        o.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
        o.Cookie.SameSite = SameSiteMode.Lax;        
    });

services.AddTwoTiersCacheTicketStore("cookies"); // use the same authenticationScheme configured in AddCookie

Similar projects

Other awesome (and more complete) projects that do something similar:

About

Two-Tier Cache implementation for .NET based on Microsoft.Extensions.Caching

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages