Pular para o conteúdo principal

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.

Fluxo de concorrência com RedisLocker

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âmetroDescrição
redisInstância de IConnectionMultiplexer
queueTimeoutTimeout máximo para workers aguardarem na fila (default: 30 min)
lockManyTickIntervalo 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

CampoDescrição
idID do recurso que será bloqueado
ttlTTL (Time To Live) do bloqueio
cancellationTokenTrue se lock já estava em uso (entrou na fila), False caso tenha sido adquirido direto

Retorno

CampoDescrição
AcquiredTrue se lock foi concedido, False se não
IdID do recurso que estava sendo bloqueado
WasInUseTrue 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}");
}
}
  • AcquireManyAsync retorna snapshots de IDs adquiridos a cada _lockManyTick.
  • Acquired = False deve ser tratado pelo consumidor.