if redis.call("get", KEYS[1]) == ARGV[1] then
redis.call("del", KEYS[2])
redis.call("lpush", KEYS[2], 1)
return redis.call("del", KEYS[1])
else
return 0
end
And here is acquire Python code:
while busy:
busy = not self._client.set(self._name, self._id, nx=True, ex=self._expire)
if busy:
if timed_out:
return False
elif blocking:
timed_out = not self._client.blpop(self._signal, blpop_timeout)
else:
logger.debug("Failed to get %r.", self._name)
return False
Imagine such situation, with two workers:
The first one acquired the lock previously.
The second is blocked by blpop() call and waiting.
Then worker-1 releases the lock, and Redis starts executing the Lua script and execution point comes to LPUSH and Redis executes LPUSH.
After that worker-2 wakes up, tries to call SET NX, which doesn't succeeds, so worker-2 calls BLPOP again and becomes blocked.
Then Redis continues execution of Lua script launched by worker-1 and calls DEL on lock's key, but it doesn't affects worker-2.
In Redis scripts are atomic. Everything is ran serially. Has this changed recently or are you using some redis-compatible implementation that is multi-threaded?
Here is current release Lua script:
And here is acquire Python code:
Imagine such situation, with two workers:
blpop()
call and waiting.LPUSH
and Redis executesLPUSH
.SET NX
, which doesn't succeeds, so worker-2 callsBLPOP
again and becomes blocked.DEL
on lock's key, but it doesn't affects worker-2.In the result - worker-2 is blocked forever.