github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/dlock/lock.lua (about) 1 -- References: 2 -- https://www.redisio.com/en/redis-lua.html 3 -- 4 -- Try to set keys with values and time to live (in seconds) if they don't exist. They will be used as a lock. 5 -- lock.lua value tokenLength ttl 6 7 -- PEXIRE key milliseconds 8 -- 1: OK 9 -- 0: Not exist or set failed. 10 local function updateLockTTL(ttl) 11 for _, k in ipairs(KEYS) do 12 redis.call("PEXPIRE", k, ttl) 13 end 14 end 15 16 -- GETRANGE key start end 17 -- If start/end is negative, it means the start position is the end of the string. 18 local function isReentrant() 19 local offset = tonumber(ARGV[2]) 20 for _, k in ipairs(KEYS) do 21 if redis.call("GETRANGE", k, 0, offset - 1) ~= string.sub(ARGV[1], 1, offset) then 22 return false 23 end 24 end 25 return true 26 end 27 28 29 -- Start to lock keys as a distributed lock. 30 local argvSet = {} 31 for _, k in ipairs(KEYS) do 32 table.insert(argvSet, k) 33 table.insert(argvSet, ARGV[1]) 34 end 35 36 -- MSETNX key1 value1 key2 value2 ... 37 -- 1: OK 38 -- 0: One of the keys exist or set failed. 39 -- 40 -- MSET key1 value1 key2 value2 ... 41 -- Always return OK. 42 -- 43 -- Check the lock if it has been occupied. 44 -- Lua scripts is atomic and can't be interrupted. 45 -- So the set key and set expire operation divded 46 -- into two steps is fine. 47 -- Redis Lua5.1 only support unpack() function, 48 -- so we can't use table.unpack() here. 49 if redis.call("MSETNX", unpack(argvSet)) == 0 then 50 return redis.error_reply("dlock occupied") 51 end 52 if not isReentrant() then 53 return redis.error_reply("dlock reentrant failed") 54 end 55 -- Really acquires a lock. 56 redis.call("MSET", unpack(argvSet)) 57 updateLockTTL(ARGV[3]) 58 return redis.status_reply("OK")