Executando verificação de segurança...
5

Biblioteca .NET para cache com PostgreSQL

GitHub: BKlug.Extensions.Caching.PostgreSql

Inspirado em usar o PostgreSql como cache e depois da leitura do post da comunidade O Postgres consegue substituir o Redis como um cache? desenvolvi a biblioteca BKlug.Extensions.Caching.PostgreSql para .Net Core.

Essa lib oferece uma implementação completa de IDistributedCache para .NET Core que utiliza tabelas UNLOGGED no PostgreSQL, sem precisar depender de serviços externos para cache em sua aplicação. A limpeza de itens expirados é realizado via pg_cron, eliminando a necessidade de threads de background no .NET e simplificando a manutenção. Também existe uma extensão de IDistributedCache com métodos genéricos para trabalhar diretamente com objetos tipados, totalmente compatível com as interfaces nativas do .Net Core.

Principais benefícios de usar essa biblioteca

  • Unificação de Infraestrutura: Se sua aplicação já utiliza PostgreSQL, manter o cache no mesmo banco reduz a complexidade operacional e aproveita recursos existentes.
  • Performance Elevada: Tabelas UNLOGGED e uso de autovacuum.
  • Limpeza Automática: Com pg_cron, itens expirados são removidos sem código adicional na aplicação, mantendo a tabela enxuta.
  • Implementação Completa de IDistributedCache: Suporta métodos síncronos e assíncronos de Get, Set, Refresh e Remove.
  • Extensões para Objetos Tipados: Métodos genéricos (SetAsync<T>, GetAsync<T>, TryGetValueAsync<T>, GetOrCreateAsync<T>)..

Configuração e Integração

1. Registro de Serviço

Simples

services.AddDistributedPostgreSqlCache(options =>
{
    options.ConnectionString = "Host=localhost;Database=cache;Username=postgres;Password=senha";
});

Avançada

services.AddDistributedPostgreSqlCache(options =>
{
    options.ConnectionString           = "Host=localhost;Database=cache;Username=postgres;Password=senha";
    options.SchemaName                 = "cache";
    options.TableName                  = "cache_items";
    options.InitializeSchema           = true;
    options.CronSchedule               = "*/5 * * * *";
    options.MinPoolSize                = 2;
    options.MaxPoolSize                = 50;
    options.ConnectionLifetime         = 600;
    options.CommandTimeout             = 60;
    options.DefaultSlidingExpiration   = TimeSpan.FromMinutes(30);
    options.UpdateOnGetCacheItem       = false;
    options.ReadOnlyMode               = false;
});

Exemplos Práticos

Interface Padrão IDistributedCache

// Armazenar string
await _cache.SetStringAsync("greeting", "Olá Mundo", 
    new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) });
// Recuperar string
string saudacao = await _cache.GetStringAsync("greeting");
// Armazenar binário
await _cache.SetAsync("dados-bin", new byte[]{1,2,3,4,5});
// Recuperar binário
byte[]? dados = await _cache.GetAsync("dados-bin");

Extensões Aprimoradas

// Armazenar objeto tipado
await _cache.SetAsync("user:42", usuario, TimeSpan.FromHours(1));
// Recuperar objeto tipado
User? u = await _cache.GetAsync<User>("user:42");
// GetOrCreate
var prod = _cache.GetOrCreate("prod:1", () => CarregarProdutoDoBanco(), options);

Inicialização Manual do Esquema

Se InitializeSchema = false, execute este script SQL manualmente:

-- Create schema and table
CREATE SCHEMA IF NOT EXISTS cache;
    
CREATE UNLOGGED TABLE IF NOT EXISTS cache.cache_items
(
    id TEXT NOT NULL PRIMARY KEY,
    value BYTEA,
    expires_at_time TIMESTAMPTZ,
    sliding_expiration_seconds DOUBLE PRECISION,
    absolute_expiration TIMESTAMPTZ
)
WITH (
    autovacuum_vacuum_scale_factor = 0.01,
    autovacuum_analyze_scale_factor = 0.005
);

CREATE INDEX IF NOT EXISTS idx_cache_items_expires
    ON cache.cache_items(expires_at_time)
    WHERE expires_at_time IS NOT NULL;

-- Create function to delete expired items
CREATE OR REPLACE FUNCTION cache.delete_expired_cache_items() 
RETURNS void LANGUAGE sql AS $$
    DELETE FROM cache.cache_items
    WHERE expires_at_time <= NOW();
$$;

-- Schedule the cleanup job (requires pg_cron extension)
-- Make sure pg_cron extension is installed first: CREATE EXTENSION IF NOT EXISTS pg_cron;
SELECT cron.schedule(
    'cache_delete_expired',
    '*/1 * * * *',  -- Run every minute (cron format: minute hour day month weekday)
    $$SELECT cache.delete_expired_cache_items()$$
);

Toda contribuição ou feedback da comunidade é muito bem-vinda para aprimorar o uso desta biblioteca!

Carregando publicação patrocinada...