Reputation: 1220
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
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