using adas_core.Application.Services.Interfaces; using Microsoft.Extensions.Logging; namespace adas_core.Application.Services.Caching { /// /// Servicio que ejecuta acciones dentro de una sección crítica controlada por lock. /// CacheService lo usará para evitar condiciones de carrera en GetOrSet. /// Funciona igual para locks locales o distribuidos. /// /// /// public class LockManagerService( ILogger logger, ILockProvider provider) { /// /// Ejecuta una función que devuelve un valor bajo un lock por clave. /// /// /// /// /// /// /// public async Task WithLockAsync( string key, TimeSpan timeout, Func> action, CancellationToken cancellationToken = default) { logger.LogDebug( "LockManager — intentando adquirir lock para key={Key} timeout={TimeoutMs}ms", key, timeout.TotalMilliseconds); var acquired = false; try { acquired = await provider.AcquireAsync(key, timeout).ConfigureAwait(false); if (!acquired) { logger.LogWarning( "LockManager — timeout al adquirir lock para key={Key} tras {TimeoutMs}ms", key, timeout.TotalMilliseconds); throw new TimeoutException($"No se pudo adquirir el lock para '{key}'"); } logger.LogDebug( "LockManager — lock adquirido correctamente para key={Key}", key); return await action().ConfigureAwait(false); } finally { if (acquired) { await SafeReleaseAsync(key); } } } /// /// Version Task (sin valor). /// /// public Task WithLockAsync( string key, TimeSpan timeout, Func action, CancellationToken cancellationToken = default) { return WithLockAsync( key, timeout, async () => { await action(); return null; }, cancellationToken); } /// /// Libera el lock y registra posibles errores sin interrumpir el flujo. /// /// private async Task SafeReleaseAsync(string key) { try { await provider.ReleaseAsync(key).ConfigureAwait(false); logger.LogDebug("LockManager — key={Key} lock deleted ", key); } catch (Exception ex) { logger.LogError(ex, "LockManager — key={Key} error deleting lock", key); } } } }