Skip to content

Instantly share code, notes, and snippets.

@oguzhaneren
Last active July 7, 2020 15:02
Show Gist options
  • Save oguzhaneren/b41354ebc65ddc977161defb4571d931 to your computer and use it in GitHub Desktop.
Save oguzhaneren/b41354ebc65ddc977161defb4571d931 to your computer and use it in GitHub Desktop.
Distributed Lock with redis (.net)
public class DistributedLock
: IAsyncDisposable
{
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 string _key;
private readonly TimeSpan? _expire;
private readonly IDatabase _redis;
private readonly string _uniqueLockKey;
private bool _isLocked;
public bool IsLocked => _isLocked;
private DistributedLock(string key, IDatabase redisDatabase, TimeSpan? expire)
{
_key = key;
_expire = expire;
_redis = redisDatabase;
_uniqueLockKey = Guid.NewGuid().ToString("N");
}
private async ValueTask TryAcquireLock()
{
_isLocked = await _redis.StringSetAsync(_key, _uniqueLockKey, _expire, When.NotExists, Flags);
}
public static async ValueTask<DistributedLock> AcquireLock(string key, IDatabase redisDatabase, TimeSpan? expire = null)
{
var distributedLock = new DistributedLock(key, redisDatabase, expire);
await distributedLock.TryAcquireLock();
return distributedLock;
}
public async ValueTask Unlock()
{
if (!_isLocked)
{
return;
}
RedisKey[] keys = {_key};
RedisValue[] values = {_uniqueLockKey};
await _redis.ScriptEvaluateAsync(AtomicReleaseScript, keys, values);
}
public ValueTask DisposeAsync()
{
return Unlock();
}
}
public static class RedisExtensions
{
public static ValueTask<DistributedLock> AcquireLock(this IDatabase redisDatabase, string key, TimeSpan? expire = null)
{
return DistributedLock.AcquireLock(key, redisDatabase, expire);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment