Is it possible to SET
redis keys without removing their existing ttl? The only way I know of at the present time is to find out the ttl and do a SETEX
but that seems less accurate.
The KEEPTTL option will be added to SET in redis>=6.0
https://github.com/antirez/redis/pull/6679
The SET command supports a set of options that modify its behavior:
EX seconds -- Set the specified expire time, in seconds.
PX milliseconds -- Set the specified expire time, in milliseconds.
NX -- Only set the key if it does not already exist.
XX -- Only set the key if it already exist.
(!) KEEPTTL -- Retain the time to live associated with the key.
According to Redis documentation, the SET
command removes the TTL because the key is overwritten.
However you can use the EVAL
command to evaluate a Lua script to do it automatically for you.
The script bellow checks for the TTL value of a key, if the value is positive it calls SETEX
with the new value and using the remaining TTL.
local ttl = redis.call('ttl', ARGV[1]) if ttl > 0 then return redis.call('SETEX', ARGV[1], ttl, ARGV[2]) end
Example:
> set key 123
OK
> expire key 120
(integer) 1
... after some seconds
> ttl key
(integer) 97
> eval "local ttl = redis.call('ttl', ARGV[1]) if ttl > 0 then return redis.call('SETEX', ARGV[1], ttl, ARGV[2]) end" 0 key 987
OK
> ttl key
96
> get key
"987"
Maybe the INCR
, INCRBY
, DECR
, etc. can help you. They don't modify the TTL.
> setex test 3600 13
OK
> incr test
(integer) 14
> ttl test
(integer) 3554
It is possible to change the value without affecting TTL on it's key by changing value bits one by one according to new value by using SETBIT.
Disadvantage of this approach however is obviously performance impact, especially if value is quite big.
NOTE: It is advisable to execute this in transaction (multi exec) block
Maintaining TTL by ourselves by
Fetching current TTL
Set new value
Restoring TTL after setting value
is obviously not advisable due to unknown durability of the commands executed.
Another alternative is to use List as data type and after adding new value to the list with LPUSH use LTRIM to maintain size of list to single element. This wont change TTL on the key.
Here's a function to check the existing TTL and use it if needed.
expire arguments 0 - use existing expire time, >0 - set a new expire time, undefined - no expire time.
/**
* update an item. preserve ttl or set a new one
* @param {object} handle the redis handle
* @param {string} key the key
* @param {*} content the content - if an object it'll get stringified
* @param {number||null} expire if a number > 0 its an expire time, 0
means keep existing ttl, undefined means no expiry
* @return {Promise}
*/
ns.updateItem = function (handle , key , content,expire) {
// first we have to get the expiry time if needed
return (expire === 0 ? handle.ttl(key) : Promise.resolve (expire))
.then (function (e) {
// deal with errors retrieving the ttl (-1 no expiry, -2 no existing record)
var ttl = e > 0 ? e : undefined;
// stingify the data if needed
var data = typeof content === "object" ? JSON.stringify(content) : content;
// set and apply ttl if needed
return ttl ? handle.set (key, data , "EX", ttl) : handle.set (key,data);
});
};
There is one way to get the remaining TTL from Redis.
Maintaining TTL in Redis :
- Get current TTL
- Set new value
- Restoring TTL after setting value
Example:
// This function helps to get the Remaining TTL from the redis.
const getRemainingTTL=(key)=> {
return new Promise((resolve,reject)=>{
redisClient.TTL(key,(err,value)=>{
if(err)reject(err);
resolve(value);
});
});
};
let remainingTTL=await getRemainingTTL(otpSetKey);
console.log('remainingTTL',remainingTTL);
redisClient.set(key,newValue,'EX',exTime ); // set agin
For some reason for me this kind of function did not work:
redisSender.set('tenMinWaitTime', avg, 'KEEPTTL')
instead I needed to write this:
redisSender.set('tenMinWaitTime', avg, { KEEPTTL: true })
documentation can be found here: https://www.npmjs.com/package//redis
© 2022 - 2024 — McMap. All rights reserved.