Skip to content

Instantly share code, notes, and snippets.

@oguzhaneren
Created July 7, 2020 15:39
Show Gist options
  • Save oguzhaneren/179925a9c114ae4482f7c60cdd2e63c7 to your computer and use it in GitHub Desktop.
Save oguzhaneren/179925a9c114ae4482f7c60cdd2e63c7 to your computer and use it in GitHub Desktop.
redis distributed lock .net
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddRedisDistributedLocker( this IServiceCollection serviceCollection,IDatabase database)
{
serviceCollection. AddSingleton<IDistributedLocker>(sp=>new RedisDistributedLocker(database));
return serviceCollection;
}
public static IServiceCollection AddRedisClientDistributedLocker( this IServiceCollection serviceCollection)
{
serviceCollection. AddSingleton<IDistributedLocker>(sp=>new RedisDistributedLocker(sp.GetRequiredService<IRedisCacheClient>().GetDbFromConfiguration().Database));
return serviceCollection;
}
}
public interface IDistributedLocker
{
Task<DistributedLock> AcquireLock(string key, TimeSpan? expire = null, CancellationToken token = default);
}
public class RedisDistributedLocker
: IDistributedLocker
{
private const string AtomicReleaseScript = @"
if redis.call(""get"",KEYS[1]) == ARGV[1] then
return redis.call(""del"",KEYS[1])
else
return 0
end
";
private const CommandFlags Flags = CommandFlags.DemandMaster;
private readonly IDatabase _redis;
public RedisDistributedLocker(IDatabase redis)
{
_redis = redis;
}
public async Task<DistributedLock> AcquireLock(string key, TimeSpan? expire = null, CancellationToken token = default)
{
var uniqueLockKey = Guid.NewGuid().ToString("N");
var isLocked = await _redis.StringSetAsync(key, uniqueLockKey, expire, When.NotExists, Flags);
return new DistributedLock(() => Unlock(key, uniqueLockKey), isLocked);
}
public async Task Unlock(string key, string uniqueLockKey)
{
RedisKey[] keys = {key};
RedisValue[] values = {uniqueLockKey};
await _redis.ScriptEvaluateAsync(AtomicReleaseScript, keys, values);
}
}
public class DistributedLock : IAsyncDisposable
{
private readonly Func<Task> _unlockFunc;
private readonly bool _isLocked;
public DistributedLock(Func<Task> unlockFunc, bool isLocked)
{
_unlockFunc = unlockFunc;
_isLocked = isLocked;
}
public async ValueTask Unlock()
{
if (_isLocked)
{
return;
}
await _unlockFunc();
}
public ValueTask DisposeAsync()
{
return Unlock();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment