Bloqueo distribuido
Crono ofrece una abstracción de bloqueo distribuido mediante las interfaces IBloqueoDistribuidoProveedor e IBloqueoDistribuido, que permiten evitar la ejecución concurrente de secciones críticas entre varios nodos (o procesos).
Conceptos
Inyecta IBloqueoDistribuidoProveedor y pídele un bloqueo identificado por una clave única. El bloqueo se adquiere dentro de un bloque using (o await using) para garantizar su liberación al finalizar:
public class MiServicio
{
private readonly IBloqueoDistribuidoProveedor _proveedor;
public MiServicio(IBloqueoDistribuidoProveedor proveedor)
{
_proveedor = proveedor;
}
public async Task ProcesarAsync(CancellationToken cancelToken)
{
var bloqueo = _proveedor.ObtenerBloqueo("mi-proceso-critico");
await using (await bloqueo.AdquirirAsync(TimeSpan.FromSeconds(30), cancelToken))
{
// Sección crítica: solo un nodo la ejecuta a la vez.
}
// El bloqueo se libera al salir del bloque.
}
}
API
IBloqueoDistribuido expone:
| Miembro | Descripción |
|---|---|
Clave | Clave que identifica de forma única el bloqueo. |
EstaRetenido() / EstaRetenidoAsync() | Comprueba si el bloqueo ya está tomado. |
Adquirir(tiempoEspera, ct) | Adquiere el bloqueo de forma síncrona; falla con TimeoutException si se agota el tiempo de espera. |
AdquirirAsync(tiempoEspera, ct) | Versión asíncrona. |
Ambos métodos de adquisición devuelven un IBloqueoManejador disposable: al liberarlo (fin del using) se libera el bloqueo.
IBloqueoDistribuidoProveedor expone ObtenerBloqueo(clave) para obtener un bloqueo por su clave.
Implementaciones
En despliegues de un solo nodo puede usarse una implementación basada en semáforos en memoria. Para entornos multi-nodo (granja de servidores) se usa una implementación distribuida real (por ejemplo, sobre Redis), registrada como singleton, de modo que el bloqueo sea efectivo entre servidores.
Usos
Varios componentes de Crono usan bloqueos para evitar condiciones de carrera. Un ejemplo típico es la generación diaria de moras: el préstamo se marca con un bloqueo antes de procesarlo, de forma que un pago que entra en paralelo no colisione con la tarea (ver también Caché, que se apoya en bloqueos para coordinar el acceso).