We use a rate limiting system which is a slight modification of rate-limit-redis npm. Instead of setting expiry after the multi transaction, which is not atomic, we introduced a 'set' command before 'incr'. The code for the same is:
options.client
.multi()
.set(<key>, 0, EX, <expiry_time>, NX)
.incr(rdskey)
.pttl(rdskey)
.exec(function (err, replies) {
if (err) {
return cb(err);
}
cb(
null,
replies[1],
replies[2]
);
});
};
This is the modification to the redis store that express will be using. As you can see, SET command sets the expiry with 'EX' only when the key doesn't already exist (NX). Expiry time is set as 1 minute. For most keys, it's working as we expect it to, but for a few keys we noticed that the key wasn't being reset even after a minute. After digging further, we saw that TTL for those keys is being set as -1
The command returns -2 if the key does not exist. The command returns -1 if the key exists but has no associated expire.
How is the TTL becoming -1 when we are setting it's expiry with SET command. Came across this stackoverflow answer where they had a similar issue with rate limiting system. They were using DECR command instead of DECR. The author of that answer also mentioned using MULTI would solve the problem, but that didn't work for us, since we are already using MULTI.
We also found that the TTL was changing to -1 when SET or INCR commands were failing in the transaction. Going through the logs, we didn't see any occurrence of SET or INCR failing. It could be possible that someone was setting the key without expiry time, but we verified that isn't happening.
Race conditions shouldn't be a problem with redis, especially with a multi transaction. What is causing some keys TTL to become -1, and how can we resolve it?