Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Atomically remove an item from set if a counter in another key is below zero?

Tags:

redis

Redis 2.0.3

In my Redis DB I have a set of items. Each item has a counter, associated with it:

MULTI
    SADD "items-set" "foo"
    INCRBY "items:foo" 10000
EXEC

New items are added to the set at random intervals.

When user does a certain action, counter is decremented:

new_counter = DECR "items:foo"

I need to atomically remove the item from the set, when the counter drops below zero (alternatively: when the counter reaches zero exactly, I can fix up logic for that.)

if new_counter < 0 then
    MULTI
        SREM "items-set" "foo"
        DEL "items:foo"
    EXEC
end

How can I do this without locking by the item name with SETNX/GETSET?

Solutions, involving change of the way I store data in Redis, are acceptable. (But, just in case, I reserve a right to counter them with some task-specific detail that I could miss in initial text.)

like image 968
Alexander Gladysh Avatar asked Feb 25 '11 15:02

Alexander Gladysh


2 Answers

just use the new WATCH capability of Redis 2.2:

WATCH items-set items:foo
count = GET items:foo
IF count == 0:
    MULTI
    SREM items-set foo
    SET items:foo count-1
    EXEC
ELSE:
    MULTI
    SET items:foo count-1
    EXEC

To understand the example you need to understand how WATCH works. Please check the doc at http://redis.io site.

p.s. there are no ways to do this with Redis 2.0.3

like image 89
antirez Avatar answered Oct 05 '22 18:10

antirez


It should be on a loop, so you can retry if someone else touched the value in the middle.

success = false
while not success 
    WATCH items-set items:foo
    count = GET items:foo
    MULTI
    IF count == 0:
        SREM items-set foo
    SET items:foo count-1
    success = EXEC
like image 35
Fernickk Avatar answered Oct 05 '22 19:10

Fernickk