Available since: 1.0.0
Time complexity: O(1)
key to hold string
key does not exist.
In that case, it is equal to
key already holds a value, no operation is performed.
SETNX is short for "SET if Not eXists".
Integer reply, specifically:
1if the key was set
0if the key was not set
Please note that:
SETcommand to acquire the lock, and a simple Lua script to release the lock. The pattern is documented in the
SETNX can be used, and was historically used, as a locking primitive. For example, to acquire the lock of the key
foo, the client could try the
SETNX lock.foo <current Unix time + lock timeout + 1>
1 the client acquired the lock, setting the
to the Unix time at which the lock should no longer be considered valid.
The client will later use
DEL lock.foo in order to release the lock.
0 the key is already locked by some other client.
We can either return to the caller if it's a non blocking lock, or enter a loop
retrying to hold the lock until we succeed or some kind of timeout expires.
In the above locking algorithm there is a problem: what happens if a client fails, crashes, or is otherwise not able to release the lock? It's possible to detect this condition because the lock key contains a UNIX timestamp. If such a timestamp is equal to the current Unix time the lock is no longer valid.
When this happens we can't just call
DEL against the key to remove the lock
and then try to issue a
SETNX, as there is a race condition here, when
multiple clients detected an expired lock and are trying to release it.
lock.footo check the timestamp, because they both received
SETNX, as the lock is still held by C3 that crashed after holding the lock.
SETNX lock.fooand it succeeds
SETNX lock.fooand it succeeds
Fortunately, it's possible to avoid this issue using the following algorithm. Let's see how C4, our sane client, uses the good algorithm:
SETNX lock.foo in order to acquire the lock
The crashed client C3 still holds it, so Redis will reply with
0 to C4.
GET lock.foo to check if the lock expired.
If it is not, it will sleep for some time and retry from the start.
Instead, if the lock is expired because the Unix time at
lock.foo is older
than the current Unix time, C4 tries to perform:
GETSET lock.foo <current Unix timestamp + lock timeout + 1>
Because of the
GETSET semantic, C4 can check if the old value stored at
key is still an expired timestamp.
If it is, the lock was acquired.
If another client, for instance C5, was faster than C4 and acquired the lock
GETSET operation, the C4
GETSET operation will return a non
C4 will simply restart from the first step.
Note that even if C4 set the key a bit a few seconds in the future this is
not a problem.
In order to make this locking algorithm more robust, a
client holding a lock should always check the timeout didn't expire before
unlocking the key with
DEL because client failures can be complex, not just
crashing but also blocking a lot of time against some operations and trying
DEL after a lot of time (when the LOCK is already held by another