Use transaction to run multiple commands atomically, but one of the commands has not been executed
Author: oik-jinnCreated Jul 7, 2023Updated Jul 13, 2023
Labelsbug
Describe the bug I use redis list to do a limiter, it works as expected most times, but recently I found that there are some keys without an expiry.Ideally, I "rpush" the value to the list and set expiry as well in one transaction, and I use "watch" as well before the transaction start.
To Reproduce I haven't reproduced the bug in my local environment, even I use jmeter to request the related api in batch, for example 1 seconds 500 requests
Expected behavior Redis transaction is atomic. Atomic means either all of the commands or none are processed. So one key should have a expiry in my case.
Versions (please complete the following information):
- Predis: v2.1.2
- PHP 7.4
- Redis Server 5.0.10
Code sample
$redisClient->watch($key);
$current = $redisClient->llen($key);
// Transaction start
$tx = $redisClient->transaction();
if ($current >= $limitNum) {
$redisClient->unwatch();
return false;
} else {
if ($redisClient->exists($key)) {
$tx->rpush($key, $now);
try {
$replies = $tx->execute();
return true;
} catch (\Exception $e) {
return false;
}
} else {
// Using transaction to let rpush and expire to be an atomic operation
$tx->rpush($key, $now);
$tx->expire($key, $expiryTime);
try {
$replies = $tx->execute();
return true;
} catch (\Exception $e) {
return false;
}
}
}Source: predis/predis