Search code examples
redissortedsetredis-py

ZREM on Redis Sorted Set


What will happen if 2 workers call ZREM on the same element of a sorted set at the same time? Will it return true to the worker which actually removes the element and false to the other to indicate it doesn't exist or will it return true to both? In other words is ZREM atomic internally?


Solution

  • Redis is (mostly) single-threaded so all its operations are atomic and ZREM is no exception. Your question, however, is actually about doing a "ZPOP" atomically so there are two possible ways to do that.

    Option 1: WATCH/MULTI/EXEC

    In pseudo code, this is how an optimistic transaction would look:

    :start
    WATCH somekey
    member = ZREVRANGE somekey 0 0
    MULTI
    ZREM somekey member
    if not EXEC goto :start // or quit trying
    

    Option 2: Lua script

    zpop.lua:
    
    local member = redis.call('ZREVRANGE', KEYS[1], 0, 0)
    return redis.call('ZREM', KEYS[1], member)
    
    redis-cli --eval zpop.lua somekey
    

    Note - The Importance of Atomicity In case you decide not to use these mechanisms that ensure atomicity, you'll be running into issues sooner than you think. Here's a possible scenario:

    Process A                Redis Server            Process B
    ZREVRANGE ------------>
              <------------- foo
                                          <--------- ZADD +inf bar
                                       OK --------->
    ZREM foo -------------->
             <-------------- 1
    

    In the above example, after A fetches foo, B inserts bar with an absurdly high score so it becomes the top element in the set. A, however, will continue and remove the previously-at-the-top foo.