5

I am using the python package (redis-py) to operate the redis database. I have a bunch of clients that set keys and values of a hash in redis. I want they set keys and values only when the hash exists. If the hash doesn't exist, setting keys and values will create the hash, which is not what I want to do.

In the redis-py page (https://github.com/andymccurdy/redis-py), the author suggested a way to do atomic operation on client side. So I wrote a similar function:

    with r.pipeline() as pipe:
        while True:
            try:
                pipe.watch("a_hash")
                if pipe.exists("a_hash"):
                    pipe.hset("a_hash", "key", "value")                  
                break
            except redis.WatchError:
                continue
            finally:
                pipe.reset()

However, this seems does not work. After I delete the hash from another client, this hash still gets created by this piece of code, so I guess this piece of code is not atomic operation. Could someone help me to identify what's the problem with this code? Or is there a better to achieve this purpose?

Appreciate your help!

1 Answer 1

5

I would suggest to read the definition of a WATCH/MULTI/EXEC block as explained in the Redis documentation.

In such block, only the commands between MULTI and EXEC are actually processed atomically (and conditionally, with an all-or-nothing semantic depending on the watch).

In your example, the EXISTS and HSET commands are not executed atomically. Actually, you don't need this atomicity: what you want is the conditional execution.

This should work better:

with r.pipeline() as pipe:
    while True:
        try:
            pipe.watch("a_hash")
            if pipe.exists("a_hash"):
                pipe.multi()
                pipe.hset("a_hash", "key", "value")
                pipe.execute()
            break
        except redis.WatchError:
            continue
        finally:
            pipe.reset()

If the key is deleted after the EXISTS but before the MULTI, the HSET will not be executed, thanks to the watch.

With Redis 2.6, a Lua server-side script is probably easier to write, and more efficient.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks Didier. One more question, what if the key is deleted after multi? Since redis is queueing commands after multi, is hset still executed in this situation?
If the key is deleted between the MULTI and HSET or even between the HSET and EXEC, then the MULTI/EXEC block is aborted, and the HSET not applied.
Is the reset() needed in this example? The docs say: "If the Pipeline is used as a context manager reset() will be called automatically."
It will be called automatically - I still prefer to make it explicit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.