2020-07-14 13:24:07 +00:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2020-11-01 19:03:21 +00:00
|
|
|
using Elsa.DistributedLock;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using RedLockNet;
|
2020-07-14 13:24:07 +00:00
|
|
|
|
2020-11-01 19:03:21 +00:00
|
|
|
namespace Elsa
|
2020-07-14 13:24:07 +00:00
|
|
|
{
|
|
|
|
|
public class RedisLockProvider : IDistributedLockProvider
|
|
|
|
|
{
|
|
|
|
|
private const string Prefix = "elsa";
|
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
private readonly TimeSpan _lockTimeout;
|
|
|
|
|
private readonly IDistributedLockFactory _distributedLockFactory;
|
2020-10-09 17:52:54 +00:00
|
|
|
private readonly List<IRedLock> _redLockInstance = new List<IRedLock>();
|
2020-07-14 13:24:07 +00:00
|
|
|
|
|
|
|
|
public RedisLockProvider(IDistributedLockFactory distributedLockFactory, TimeSpan lockTimeout, ILogger<RedisLockProvider> logger)
|
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_distributedLockFactory = distributedLockFactory;
|
|
|
|
|
_lockTimeout = lockTimeout;
|
|
|
|
|
}
|
|
|
|
|
public Task<bool> AcquireLockAsync(string name, CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
|
|
|
|
if (name == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CreateLockAsync(name, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
private async Task<bool> CreateLockAsync(string name, CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
|
|
|
|
var resourceName = $"{Prefix}:{name}";
|
2020-07-15 09:52:53 +00:00
|
|
|
_logger.LogInformation("Lock provider will try to acquire lock for {resourceName}",resourceName);
|
2020-07-14 13:24:07 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var redLock = await _distributedLockFactory.CreateLockAsync(resourceName, _lockTimeout,
|
|
|
|
|
TimeSpan.FromSeconds(1),
|
|
|
|
|
TimeSpan.FromMilliseconds(10),
|
|
|
|
|
cancellationToken)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
if (redLock.IsAcquired)
|
|
|
|
|
{
|
|
|
|
|
|
2020-10-09 17:52:54 +00:00
|
|
|
lock (_redLockInstance)
|
2020-07-14 13:24:07 +00:00
|
|
|
{
|
2020-10-09 17:52:54 +00:00
|
|
|
_redLockInstance.Add(redLock);
|
2020-07-14 13:24:07 +00:00
|
|
|
}
|
2020-07-15 09:52:53 +00:00
|
|
|
_logger.LogInformation("Lock provider acquired lock for {resourceName}",resourceName);
|
2020-07-14 13:24:07 +00:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2020-07-15 09:52:53 +00:00
|
|
|
_logger.LogWarning("Failed to acquire lock for {resourceName}. Reason > {ex}",resourceName,ex);
|
2020-07-14 13:24:07 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task ReleaseLockAsync(string name, CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
|
|
|
|
if (name == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(name));
|
|
|
|
|
}
|
|
|
|
|
var resourceName = $"{Prefix}:{name}";
|
|
|
|
|
|
2020-07-15 09:52:53 +00:00
|
|
|
_logger.LogInformation("Lock provider will try to release lock for {resourceName}", resourceName);
|
|
|
|
|
|
2020-07-14 13:24:07 +00:00
|
|
|
try
|
|
|
|
|
{
|
2020-10-09 17:52:54 +00:00
|
|
|
lock (_redLockInstance)
|
2020-07-14 13:24:07 +00:00
|
|
|
{
|
2020-10-09 17:52:54 +00:00
|
|
|
foreach (var redLock in _redLockInstance)
|
2020-07-14 13:24:07 +00:00
|
|
|
{
|
|
|
|
|
if (redLock.Resource == $"{Prefix}:{name}")
|
|
|
|
|
{
|
|
|
|
|
redLock.Dispose();
|
2020-10-09 17:52:54 +00:00
|
|
|
_redLockInstance.Remove(redLock);
|
2020-07-15 09:52:53 +00:00
|
|
|
_logger.LogInformation("Lock provider released lock for {resourceName}",resourceName);
|
|
|
|
|
|
2020-07-14 13:24:07 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2020-07-15 09:52:53 +00:00
|
|
|
_logger.LogWarning("Failed to release lock for {resourceName}. Reason > {ex}",resourceName,ex);
|
2020-07-14 13:24:07 +00:00
|
|
|
}
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|