I'm using a redis list to make a limiter, and it works as expected most of the time, but recently I've discovered that there are some keys that don't have an expiration time. Ideally I would "rpush" the values into the list and set the expiry time in a transaction and I would also use "watch" before the transaction starts.
This bug does not reproduce in my local environment, even if I use jmeter to batch request related APIs, such as 500 requests in 1 second
Prediction: v2.1.2 PHP 7.4 Redis server 5.0.10
$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; } } }
This is the expected operation in my local Redis server
Redis transactions are atomic. Atomic means either all commands are processed or no commands are processed. So in my case a key should have an expiration date.
Redis transactions are not such atomic transactions. They are atomic because no other process can access the key space while the transactions in the command are executing. If a command within a transaction fails, subsequent commands will be executed and will not be rolled back.
For example, let's execute a transaction that contains the wrong command:
Here we check if the list exists and add some initial items to it. Then, within the transaction, we pop an item from the list, mistakenly try to add a new item, think that key
mylist
owns a collection, and then set keymylist. The first and third commands succeed, and finally,
mylist
sets the time to live. The second command fails. There is no rollback functionality built into Redis for this - your application needs to use optimistic locking via thewatch
command... This is to detect changes by other processes before your transaction gets the changes your transaction wants to make The key has exclusive access to the server. It is not a rollback mechanism.Details:https://redis.io/docs/interact/transactions/