qkhhly
qkhhly

Reputation: 1220

Redis, only allow operation on existing keys

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!

Upvotes: 5

Views: 5716

Answers (1)

Didier Spezia
Didier Spezia

Reputation: 73306

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.

Upvotes: 5

Related Questions