Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🗄️ dotnet-hybrid-cache

Demonstração do Hybrid Caching no .NET 10 — o melhor dos dois mundos

.NET 10 C# 13 HybridCache Redis ASP.NET Core License MIT


📖 Sobre o projeto

Projeto de demonstração que combina cache em memória (L1) com cache distribuído (L2) usando a nova API HybridCache do .NET 10. O objetivo é reduzir latência, aliviar o cache distribuído e simplificar o código de stampede prevention com uma única abstração.

O HybridCache no .NET 10 unifica IMemoryCache e IDistributedCache em uma única API coesa, oferecendo:

  • ✅ Busca primeiro no cache local (in-process), depois no distribuído
  • ✅ Prevenção nativa de cache stampede (sem código extra)
  • ✅ TTLs independentes para L1 e L2
  • ✅ Invalidação por chave ou por tags
  • ✅ Suporte a serialização customizada

🏗️ Arquitetura

Request
  │
  ├─► [L1] In-Memory (IMemoryCache)
  │     └── HIT → retorna (~µs, sem serialização)
  │     └── MISS ↓
  │
  ├─► [L2] Distribuído (Redis)
  │     └── HIT → preenche L1 e retorna
  │     └── MISS ↓
  │
  └─► Factory (banco de dados / API externa)
        └── Resultado armazenado em L1 + L2
Camada Tecnologia TTL padrão Escopo
L1 IMemoryCache (in-process) 30 segundos Por instância
L2 Redis 7+ 5 minutos Compartilhado entre pods

✅ Pré-requisitos

  • .NET 10 SDK (preview ou superior)
  • Docker (para Redis local)
  • Redis 7+ ou Azure Cache for Redis
  • Visual Studio 2022 / JetBrains Rider / VS Code

🚀 Como executar

1. Subir o Redis local com Docker

docker run -d -p 6379:6379 --name redis-local redis:7-alpine

Ou usando o docker-compose.yml do projeto:

docker compose up -d

2. Restaurar dependências e executar

dotnet restore
dotnet run --project src/Caching.WebApi

3. Acessar o Swagger

https://localhost:5001/swagger/index.html

📁 Estrutura do projeto

dotnet-hybrid-cache/
├── src/
│   ├── Caching.WebApi/             # Endpoints, Program.cs e configuração
│   ├── Caching.Domain/             # Serviços de domínio e interfaces
│   └── Caching.Infra.Cache/        # Redis, repositórios e serialização
├── test/
│   └── Caching.UnitTest/           # Testes de integração e benchmark
├── docker-compose.yml
├── .editorconfig
└── README.md

⚙️ Configuração

appsettings.json

{
  "ConnectionStrings": {
    "Redis": [
      {
        "Name": "Caching",
        "ConnectionString": "127.0.0.1:6379,defaultDatabase=0"
      }
    ]
  }
}

🔧 Registro de dependências

// Startup.cs

// L2: Redis distribuído
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = settings.ConnectionStrings.Redis.FirstOrDefault(c => c.Name.Equals("Caching"))?.ConnectionString;
});

// HybridCache — combina L1 + L2 automaticamente
builder.Services.AddHybridCache(options =>
{
    options.DefaultEntryOptions = new HybridCacheEntryOptions
    {
        Expiration            = TimeSpan.FromMinutes(5),  // TTL no L2 (Redis)
        LocalCacheExpiration  = TimeSpan.FromSeconds(30)  // TTL no L1 (memória)
    };
});

💡 Uso no serviço

Busca com GetOrCreateAsync

public class ProductService(HybridCache cache, IProductRepository repo)
{
    public async Task<Product?> GetByIdAsync(
        int id, CancellationToken ct = default)
    {
        return await cache.GetOrCreateAsync(
            key:     $"product:{id}",
            factory: async token =>
                         await repo.FindAsync(id, token),
            cancellationToken: ct
        );
    }
}

Invalidação por chave

public async Task InvalidateAsync(int id)
    => await cache.RemoveAsync($"product:{id}");

Invalidação por tags

// Armazenar com tags
await cache.GetOrCreateAsync(
    key:     $"product:{id}",
    factory: async token => await repo.FindAsync(id, token),
    options: new HybridCacheEntryOptions { Tags = ["products"] },
    cancellationToken: ct
);

// Invalidar todos os itens com a tag "products"
await cache.RemoveByTagAsync("products");

🧪 Testes

dotnet test tests/HybridCacheDemo.Tests

Os testes de integração usam TestContainers para provisionar um Redis real em Docker durante a execução.


📊 Comparativo

Cenário Antes (manual) Depois (HybridCache)
Código de cache ~40 linhas por serviço 5 linhas
Stampede prevention Implementação manual Nativo
Invalidação distribuída Pub/Sub manual RemoveAsync / RemoveByTagAsync
TTL independente L1/L2 Não suportado LocalCacheExpiration
Serialização Configuração separada Centralizada

📚 Referências


📝 Licença

Distribuído sob a licença MIT. Consulte o arquivo LICENSE para mais detalhes.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages