Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How compareAndSet works internally in redis

Tags:

java

spring

redis

spring-data-redis module contains RedisAtomicLong class.

In this class you can see

public boolean compareAndSet(long expect, long update) {

    return generalOps.execute(new SessionCallback<Boolean>() {

        @Override
        @SuppressWarnings("unchecked")
        public Boolean execute(RedisOperations operations) {
            for (;;) {
                operations.watch(Collections.singleton(key));
                if (expect == get()) {
                    generalOps.multi();
                    set(update);
                    if (operations.exec() != null) {
                        return true;
                    }
                }
                {
                    return false;
                }
            }
        }
    });
}

My question is why it works?

generalOps.multi() starts transaction after get() is invoked. It means that there is possibility that two different thread (or even client) can change value and both of them will succeed.

Is operations.watch prevent it somehow? JavaDoc doesn't explain purpose of this method.

PS: Minor question: why for (;;)? There is always one iteration.

like image 423
talex Avatar asked Oct 12 '25 05:10

talex


2 Answers

Q: Is operations.watch prevent it somehow?

YES.

Quoting from Redis documentation about transaction:

WATCH is used to provide a check-and-set (CAS) behavior to Redis transactions.

WATCHed keys are monitored in order to detect changes against them. If at least one watched key is modified before the EXEC command, the whole transaction aborts, and EXEC returns a Null reply to notify that the transaction failed.

You can learn more about Redis transaction from that documentation.

Q: why for (;;)? There is always one iteration.

It seems the code you've posted is very old. From Google's cache of this url, I saw the code you provided which is dated back to Oct 15th, 2012!

Latest codes look much different:

  • compareAndSet method
  • CompareAndSet class
like image 90
sazzad Avatar answered Oct 14 '25 19:10

sazzad


Is operations.watch prevent it somehow?

YES. After watching a key, if the key has been modified before transaction finishes, EXEC will fail. So if EXEC successes, the value is guaranteed to be unchanged by others.

why for (;;)? There is always one iteration.

In your case, it seems the infinite loop is redundant.

However, if you want to implement a check-and-set operation to modify the value with the old value, the infinite loop is necessary. Check this example from redis doc:

WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC

Since EXEC might fail, you need to retry the whole process in a loop until it successes.

like image 30
for_stack Avatar answered Oct 14 '25 19:10

for_stack