modelRT/distributedlock/luascript/rlock_script.go

108 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package luascript defines the lua script used for redis distributed lock
package luascript
// RlockScript is the lua script for the lock read lock command
/*
KEYS[1]锁的键名key,通常是锁的唯一标识。
KEYS[2]锁的超时键名前缀rwTimeoutPrefix,用于存储每个读锁的超时键。
ARGV[1]锁的过期时间lockLeaseTime,单位为毫秒。
ARGV[2]当前客户端的唯一标识token,用于区分不同的客户端。
*/
var RlockScript = `local mode = redis.call('hget', KEYS[1], 'mode');
local lockKey = KEYS[2] .. ARGV[2];
if (mode == false) then
redis.call('hset', KEYS[1], 'mode', 'read');
redis.call('hset', KEYS[1], lockKey, '1');
redis.call('hexpire', KEYS[1], ARGV[1] 'fields' '1' lockKey);
redis.call('expire', KEYS[1], ARGV[1]);
return 1;
end;
if (mode == 'write') then
return -1;
end;
if (mode == 'read') then
if (redis.call('exists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], lockKey, '1');
local remainTime = redis.call('httl', KEYS[1], 'fields', '1', lockKey);
redis.call('hexpire', key, math.max(remainTime, ARGV[1]));
else
redis.call('hset', KEYS[1], lockKey, '1');
redis.call('hexpire', KEYS[1], lockKey, ARGV[1]);
end;
local cursor = 0;
local maxRemainTime = tonumber(ARGV[1]);
local pattern = KEYS[2] .. ':*';
repeat
local hscanResult = redis.call('hscan', KEYS[1], cursor, 'match', pattern, 'count', '100');
cursor = tonumber(hscanResult[1]);
local fields = hscanResult[2];
for i = 1, #fields,2 do
local field = fields[i];
local remainTime = redis.call('httl', KEYS[1], 'fields', '1', field);
maxRemainTime = math.max(tonumber(remainTime[1]), maxRemainTime);
end;
until cursor == 0;
local remainTime = redis.call('ttl', KEYS[1]);
redis.call('expire', KEYS[1], math.max(tonumber(remainTime),maxRemainTime));
return 1;
end;
`
// TODO 优化读锁解锁语句
// UnRlockScript is the lua script for the unlock read lock command
/*
KEYS[1]:锁的键名key,通常是锁的唯一标识。
KEYS[2]:锁的释放通知频道chankey,用于通知其他客户端锁已释放。
KEYS[3]:锁的超时键名前缀rwTimeoutTokenPrefix,用于存储每个读锁的超时键。
KEYS[4]:锁的超时键名前缀prefixKey,用于存储每个读锁的超时键。
ARGV[1]:解锁消息unlockMessage,用于通知其他客户端锁已释放。
ARGV[2]:当前客户端的唯一标识token,用于区分不同的客户端。
*/
var UnRlockScript = `local mode = redis.call('hget', KEYS[1], 'mode');
if (mode == false) then
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
local lockExists = redis.call('hexists', KEYS[1], ARGV[2]);
if (lockExists == 0) then
return nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1);
if (counter == 0) then
redis.call('hdel', KEYS[1], ARGV[2]);
end;
redis.call('del', KEYS[3] .. ':' .. (counter+1));
if (redis.call('hlen', KEYS[1]) > 1) then
local maxRemainTime = -3;
local keys = redis.call('hkeys', KEYS[1]);
for n, key in ipairs(keys) do
counter = tonumber(redis.call('hget', KEYS[1], key));
if type(counter) == 'number' then
for i=counter, 1, -1 do
local remainTime = redis.call('ttl', KEYS[4] .. ':' .. key .. ':rwlock_timeout:' .. i);
maxRemainTime = math.max(remainTime, maxRemainTime);
end;
end;
end;
if maxRemainTime > 0 then
redis.call('pexpire', KEYS[1], maxRemainTime);
return 0;
end;
if mode == 'write' then
return 0;
end;
end;
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
`