Redis BITSET and WATCH

2019-08-16 18:30发布

问题:

I'm using Redis to create an algorithm for claiming unused integers from a range. My solution is based on the answer I got for this SO question.

This solution uses BITPOS and BITSET, and to avoid race conditions, I also use WATCH/MULTI/EXEC. In order to test the concurrency aspects I created a bash script that concurrently attempts to find a free number 10 times in parallel, to investigate the possible outcomes of the EXEC command.

What I found was that EXEC never returned null, even when the watched key was modified by another client. I added delays such that there was plenty of time to provoke a concurrent modification that should trigger the watch mechanism so that EXEC fails, but it didn't.

So basically I had this piece of code:

while (true) {
  WATCH mykey
  number = BITPOS mykey, 0
  if (number > maxNumber) THROW ERROR

  (deliberate delay)

  MULTI
  SETBIT mykey, number, 1
  if EXEC != null return number
}

and also a loop that calls SETBIT mykey, N, 1 for N = 1..10, in 10 different processes.

What I found was that EXEC never returned null, even when the key was definitely modified by another process during the watched period of time.

Questions:

  1. Is WATCH simply not supported for BIT based Redis commands?
  2. If it is supported, why wasn't it triggered under these circumstances? Am I testing/provoking it incorrectly? As I understand it, WATCH should make EXEC fail if the key has been modified by another client/connection during the watched period of time, and calling this from 10 different Linux processes, each creating its own connection, seems to fit that requirement?
  3. In this particular case, does WATCH and MULTI actually offer anything? BITSET returns the previous value of that bit, so shouldn't atomicity be guaranteed simply by the following pseudo-code algorithm:
    while (true) {
      number = BITPOS mykey, 0
      if (number > maxNumber) THROW ERROR

      wasUsed = SETBIT mykey, number, 1

      if (!wasUsed) {
        return number
      }
    }

回答1:

  1. There's no documentation suggesting that WATCH doesn't support the bit setting commands.

  2. Your code looks right to me, so it's hard to say why it isn't working. To investigate it further you'd have to provide a MCVE rather than pseudocode. However...

  3. You're right, you don't need a transaction here, this algorithm should guarantee atomicity.