SETNX key value

    Time complexity: O(1)

    Set to hold string value if key does not exist. In that case, it is equal to . When key already holds a value, no operation is performed. SETNX is short for "SET if Not eXists".

    , specifically:

    • 1 if the key was set
    • 0 if the key was not set

    redis> SETNX mykey "Hello"

    redis> SETNX mykey "World"

    redis>

    Please note that:

    • We document the old pattern anyway because certain existing implementations link to this page as a reference. Moreover it is an interesting example of how Redis commands can be used in order to mount programming primitives.
    • Anyway even assuming a single-instance locking primitive, starting with 2.6.12 it is possible to create a much simpler locking primitive, equivalent to the one discussed here, using the SET command to acquire the lock, and a simple Lua script to release the lock. The pattern is documented in the command page.That said, 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 following:

    If returns the client acquired the lock, setting the lock.foo key 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.

    If SETNX returns 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 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.

    • C1 and C2 read lock.foo to check the timestamp, because they both received 0 after executing , as the lock is still held by C3 that crashed after holding the lock.
    • C1 sends DEL lock.foo
    • C1 sends and it succeeds
    • C2 sends DEL lock.foo
    • ERROR: both C1 and C2 acquired the lock because of the race condition.
    • C4 sends SETNX lock.foo in order to acquire the lock

    • The crashed client C3 still holds it, so Redis will reply with 0 to C4.

    • C4 sends 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:

    • Because of the GETSET semantic, C4 can check if the old value stored at is still an expired timestamp. If it is, the lock was acquired.

    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 because client failures can be complex, not just crashing but also blocking a lot of time against some operations and trying to issue DEL after a lot of time (when the LOCK is already held by another client).