diff --git a/src/DistributedLockManager/Configuration.cs b/src/DistributedLockManager/Configuration.cs index ed26eba..df11891 100644 --- a/src/DistributedLockManager/Configuration.cs +++ b/src/DistributedLockManager/Configuration.cs @@ -1,6 +1,11 @@ using DistributedLockManager.Interfaces; using DistributedLockManager.Services; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using RedLockNet; +using RedLockNet.SERedis; +using RedLockNet.SERedis.Configuration; +using StackExchange.Redis; namespace DistributedLockManager; @@ -8,6 +13,14 @@ public static class CacheManagerConfiguration { public static void AddDistributedLockManager(this IServiceCollection services) { + services.AddSingleton(sp => + { + var connectionMultiplexer = sp.GetRequiredService(); + var loggerFactory = sp.GetRequiredService(); + + return RedLockFactory.Create([new RedLockMultiplexer(connectionMultiplexer)], loggerFactory); + }); + services.AddScoped(); services.AddScoped(typeof(IDistributedLockService<>), typeof(DistributedLockService<>)); } diff --git a/src/DistributedLockManager/Services/DistributedLockService.cs b/src/DistributedLockManager/Services/DistributedLockService.cs index 9fea6b9..3d6c8b6 100644 --- a/src/DistributedLockManager/Services/DistributedLockService.cs +++ b/src/DistributedLockManager/Services/DistributedLockService.cs @@ -1,25 +1,14 @@ using DistributedLockManager.Interfaces; -using RedLockNet.SERedis; -using RedLockNet.SERedis.Configuration; -using StackExchange.Redis; +using RedLockNet; namespace DistributedLockManager.Services; -public class DistributedLockService : IDistributedLockService +public class DistributedLockService(IDistributedLockFactory distributedLockFactory) : IDistributedLockService { - private readonly RedLockFactory _redLockFactory; - public DistributedLockService(IConnectionMultiplexer connectionMultiplexer) - { - var endPoints = connectionMultiplexer.GetEndPoints(); - List redLockEndPoints = [.. endPoints]; - - _redLockFactory = RedLockFactory.Create(redLockEndPoints); - } - public async Task RunWithLockAsync(Func func, string key, CancellationToken cancellationToken, int expiryInSecond = 30, int waitInSecond = 10, int retryInSecond = 1) { // blocks until acquired or 'wait' timeout - await using var redLock = await _redLockFactory.CreateLockAsync(key, TimeSpan.FromSeconds(expiryInSecond), TimeSpan.FromSeconds(waitInSecond), + await using var redLock = await distributedLockFactory.CreateLockAsync(key, TimeSpan.FromSeconds(expiryInSecond), TimeSpan.FromSeconds(waitInSecond), TimeSpan.FromSeconds(retryInSecond), cancellationToken); if (redLock.IsAcquired) @@ -29,7 +18,7 @@ public async Task RunWithLockAsync(Func func, string key, CancellationToke public async Task RunWithLockAsync(Task task, string key, CancellationToken cancellationToken, int expiryInSecond = 30, int waitInSecond = 10, int retryInSecond = 1) { // blocks until acquired or 'wait' timeout - await using var redLock = await _redLockFactory.CreateLockAsync(key, TimeSpan.FromSeconds(expiryInSecond), TimeSpan.FromSeconds(waitInSecond), + await using var redLock = await distributedLockFactory.CreateLockAsync(key, TimeSpan.FromSeconds(expiryInSecond), TimeSpan.FromSeconds(waitInSecond), TimeSpan.FromSeconds(retryInSecond), cancellationToken); if (redLock.IsAcquired) @@ -37,21 +26,12 @@ public async Task RunWithLockAsync(Task task, string key, CancellationToken canc } } -public class DistributedLockService : IDistributedLockService +public class DistributedLockService(IDistributedLockFactory distributedLockFactory) : IDistributedLockService { - private readonly RedLockFactory _redLockFactory; - public DistributedLockService(IConnectionMultiplexer connectionMultiplexer) - { - var endPoints = connectionMultiplexer.GetEndPoints(); - List redLockEndPoints = [.. endPoints]; - - _redLockFactory = RedLockFactory.Create(redLockEndPoints); - } - public async Task RunWithLockAsync(Func> func, string key, CancellationToken cancellationToken, int expiryInSecond = 30, int waitInSecond = 10, int retryInSecond = 1) { // blocks until acquired or 'wait' timeout - await using var redLock = await _redLockFactory.CreateLockAsync(key, + await using var redLock = await distributedLockFactory.CreateLockAsync(key, expiryTime: TimeSpan.FromSeconds(expiryInSecond), waitTime: TimeSpan.FromSeconds(waitInSecond), retryTime: TimeSpan.FromSeconds(retryInSecond), cancellationToken); @@ -66,7 +46,7 @@ public async Task RunWithLockAsync(Func> func, string key public async Task RunWithLockAsync(Task task, string key, CancellationToken cancellationToken, int expiryInSecond = 30, int waitInSecond = 10, int retryInSecond = 1) { // blocks until acquired or 'wait' timeout - await using var redLock = await _redLockFactory.CreateLockAsync(key, TimeSpan.FromSeconds(expiryInSecond), TimeSpan.FromSeconds(waitInSecond), + await using var redLock = await distributedLockFactory.CreateLockAsync(key, TimeSpan.FromSeconds(expiryInSecond), TimeSpan.FromSeconds(waitInSecond), TimeSpan.FromSeconds(retryInSecond), cancellationToken); if (redLock.IsAcquired) diff --git a/tests/DistributedLockManager.Tests/DistributedLockServiceIntegrationTests.cs b/tests/DistributedLockManager.Tests/DistributedLockServiceIntegrationTests.cs index 94b5322..f1da191 100644 --- a/tests/DistributedLockManager.Tests/DistributedLockServiceIntegrationTests.cs +++ b/tests/DistributedLockManager.Tests/DistributedLockServiceIntegrationTests.cs @@ -17,33 +17,39 @@ public class DistributedLockServiceIntegrationTests { private IServiceScope? scope; private IContainer? _redisContainer; + private IConnectionMultiplexer? _connectionMultiplexer; private const string RedisImage = "redis:latest"; private const int RedisPort = 6379; [OneTimeSetUp] public async Task OneTimeSetUp() { - _redisContainer = new ContainerBuilder() - .WithImage(RedisImage) + _redisContainer = new ContainerBuilder(RedisImage) .WithCleanUp(true) .WithName($"dtm-redis-{Guid.NewGuid():N}") .WithPortBinding(RedisPort, true) + .WithWaitStrategy(Wait.ForUnixContainer().UntilExternalTcpPortIsAvailable(RedisPort)) .Build(); if (_redisContainer == null) Assert.Fail("Redis container not started"); await _redisContainer!.StartAsync(); + + // Wait a bit for Redis to be fully ready + await Task.Delay(1000); + var container = _redisContainer!; var host = container.Hostname; var port = container.GetMappedPublicPort(RedisPort); var endpoint = $"{host}:{port}"; // create the connection multiplexer and register it in DI - using var mux = await ConnectionMultiplexer.ConnectAsync(endpoint); + _connectionMultiplexer = await ConnectionMultiplexer.ConnectAsync(endpoint); var services = new ServiceCollection(); + services.AddLogging(); services.AddDistributedLockManager(); - services.AddSingleton(mux); + services.AddSingleton(_connectionMultiplexer); var provider = services.BuildServiceProvider(); @@ -53,6 +59,9 @@ public async Task OneTimeSetUp() [OneTimeTearDown] public async Task OneTimeTearDown() { + scope?.Dispose(); + _connectionMultiplexer?.Dispose(); + if (_redisContainer is not null) await _redisContainer.StopAsync(); }