RedisLocker
RedisLocker é uma classe para gerenciamento de locks distribuídos utilizando Redis. Ela permite adquirir locks para IDs específicos (pedido), gerenciar filas de espera e notificar workers quando um lock é liberado. A classe é genérica e não faz retry automático; o tratamento de Acquired = false deve ser feito pelo consumidor.

Responsabilidades
- Garantir locks distribuídos sobre IDs (pedido) usando Redis.
- Gerenciar uma fila de espera para locks já adquiridos.
- Notificar outros workers quando um lock é liberado.
- Suportar aquisição em batch (AcquireManyAsync) com snapshots periódicos.
Pontos fortes:
- Usa Redis de forma adequada para locks (StringSet com NX e TTL) e filas (SortedSet).
- TTL do lock e timeout da fila parametrizáveis.
- Uso de IAsyncEnumerable em AcquireManyAsync é interessante para streaming de resultados.
Desvantagens
- Requer uma instância Redis
- Requer
Uso
Construtor
| Parâmetro | Descrição |
|---|---|
| redis | Instância de IConnectionMultiplexer |
| queueTimeout | Timeout máximo para workers aguardarem na fila (default: 30 min) |
| lockManyTick | Intervalo de flush para AcquireManyAsync (default: 50 ms) |
Inicialização
var redis = ConnectionMultiplexer.Connect("localhost:6379");
// Cria instância do RedisLocker
var locker = new RedisLocker(
redis,
queueTimeout: TimeSpan.FromMinutes(30), // opcional
lockManyTick: TimeSpan.FromMilliseconds(50) // opcional
);
Aquisição de Lock único
var ttl = TimeSpan.FromSeconds(10);
var result = await locker.AcquireAsync("pedido-123", ttl);
if (result.Acquired)
{
// Lock adquirido com sucesso
}
else
{
// Lock não adquirido; pode estar em uso e excedeu o timeout ou erro de instância/rede
// Consumidor decide se tenta novamente
}
Parâmetros
| Campo | Descrição |
|---|---|
id | ID do recurso que será bloqueado |
ttl | TTL (Time To Live) do bloqueio |
cancellationToken | True se lock já estava em uso (entrou na fila), False caso tenha sido adquirido direto |
Retorno
| Campo | Descrição |
|---|---|
| Acquired | True se lock foi concedido, False se não |
| Id | ID do recurso que estava sendo bloqueado |
| WasInUse | True se lock já estava em uso (entrou na fila), False caso tenha sido adquirido direto |
Aquisição de multiplos Locks (Batch)
var ids = new List<string> { "pedido-1", "pedido-2", "pedido-3" };
var ttl = TimeSpan.FromSeconds(10);
await foreach (var snapshot in locker.AcquireManyAsync(ids, ttl))
{
foreach (var id in snapshot)
{
Console.WriteLine($"Lock adquirido para {id}");
}
}
AcquireManyAsyncretorna snapshots de IDs adquiridos a cada _lockManyTick.Acquired = Falsedeve ser tratado pelo consumidor.