You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
83 lines
3.2 KiB
83 lines
3.2 KiB
using StackExchange.Redis;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Easy.Realization;
|
|
|
|
internal static class IDatabaseExtension
|
|
{
|
|
#region Distributed Locker
|
|
internal static async Task<(bool Success, string LockValue)> LockAsync(this IDatabase redisDb, string cacheKey, int timeoutSeconds = 5, bool autoDelay = true)
|
|
{
|
|
|
|
var lockKey = GetLockKey(cacheKey);
|
|
var lockValue = Guid.NewGuid().ToString();
|
|
var timeoutMilliseconds = timeoutSeconds * 1000;
|
|
var expiration = TimeSpan.FromMilliseconds(timeoutMilliseconds);
|
|
bool flag = await redisDb.StringSetAsync(lockKey, lockValue, expiration, When.NotExists, CommandFlags.None);
|
|
if (flag && autoDelay)
|
|
{
|
|
var refreshMilliseconds = (int)(timeoutMilliseconds / 2.0);
|
|
var autoDelayTimer = new Timer(timerState => Delay(redisDb, lockKey, lockValue, timeoutMilliseconds), null, refreshMilliseconds, refreshMilliseconds);
|
|
var addResult = AutoDelayTimers.TryAdd(lockKey, autoDelayTimer);
|
|
if (!addResult)
|
|
{
|
|
autoDelayTimer?.Dispose();
|
|
await redisDb.SafedUnLockAsync(cacheKey, lockValue);
|
|
return (false, null);
|
|
}
|
|
}
|
|
return (flag, flag ? lockValue : null);
|
|
}
|
|
|
|
internal static async Task<bool> SafedUnLockAsync(this IDatabase redisDb, string cacheKey, string lockValue)
|
|
{
|
|
var lockKey = GetLockKey(cacheKey);
|
|
AutoDelayTimers.CloseTimer(lockKey);
|
|
|
|
var script = @"local invalue = @value
|
|
local currvalue = redis.call('get',@key)
|
|
if(invalue==currvalue) then redis.call('del',@key)
|
|
return 1
|
|
else
|
|
return 0
|
|
end";
|
|
var parameters = new { key = lockKey, value = lockValue };
|
|
var prepared = LuaScript.Prepare(script);
|
|
var result = (int)await redisDb.ScriptEvaluateAsync(prepared, parameters);
|
|
return result == 1;
|
|
}
|
|
|
|
internal static void Delay(IDatabase redisDb, string key, string value, int milliseconds)
|
|
{
|
|
if (!AutoDelayTimers.ContainsKey(key))
|
|
return;
|
|
|
|
// local ttltime = redis.call('PTTL', @key)
|
|
var script = @"local val = redis.call('GET', @key)
|
|
if val==@value then
|
|
redis.call('PEXPIRE', @key, @milliseconds)
|
|
return 1
|
|
end
|
|
return 0";
|
|
object parameters = new { key, value, milliseconds };
|
|
var prepared = LuaScript.Prepare(script);
|
|
var result = redisDb.ScriptEvaluateAsync(prepared, parameters, CommandFlags.None).GetAwaiter().GetResult();
|
|
if ((int)result == 0)
|
|
{
|
|
AutoDelayTimers.CloseTimer(key);
|
|
}
|
|
return;
|
|
}
|
|
|
|
internal static string GetLockKey(string cacheKey)
|
|
{
|
|
return $"adnc:locker:{cacheKey.Replace(":", "-")}";
|
|
}
|
|
|
|
#endregion Distributed Locker
|
|
|
|
}
|
|
|