antirez/redis — Issue #152: Implement HGETSET
Author: antirez
Created: 2011-10-20 07:49:49+00:00
State: open
Labels: feature request, waiting user input

We already have GETSET operating on normal keys, however there is no equivalent for hashes, even if hash fields also support increments via HINCRBY. HGETALL could be an interesting addition both to perform reset-and-get operations on counters, and to solve concurrency issues especially in a redis cluster environment where single fast commands are to be preferred.

An example of GETSET to avoid a concurrency problem is the following:

* We have hashes representing users.
* Users have an associated karma.
* We give an user a point of karma every time he visits the site, but not before three hours after the latest karma increment.

So in order to model this problem we can have two fields in the hash representing our users.

* One field is the karma itself. We increment it with HINCRBY.
* One field is the last time we incremented the karma. This field is called karma_incr_time.

The naive algorithm would be, in pseudo code:

```
function increment_karma_if_needed
redis.hget(user_hash, "karma_incr_time")
if enough_time_elapsed
redis.incrby(user_hash,"karma")
redis.set(user_hash,"karma_incr_time",current_time())
end
end
```

However there is an obvious race condition in the above code as two instances may read the old karma_incr_time field at the same time. Both will increment the karma. With HGETALL this race condition can be fixed in the following way:

```
function increment_karma_if_needed
redis.hget(user_hash, "karma_incr_time")
if enough_time_elapsed
incr_time = redis.getset(user_hash,"karma_incr_time",current_time())
if incr_time is still old enough (was not updated in the meantime)
redis.incrby(user_hash,"karma")
end
end
end
```

With the above change only the first instance will win, since it will set the increment time to a value that is now too in the future for the increment to be performed.

Please add your thoughts about adding this feature or not.