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")