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:
- Is
WATCH
simply not supported for BIT based Redis commands? - If it is supported, why wasn't it triggered under these circumstances? Am I testing/provoking it incorrectly? As I understand it,
WATCH
should makeEXEC
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? - In this particular case, does
WATCH
andMULTI
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
}
}