Redis atomic GET and EXPIRE - redis

Is there an atomic GET + EXPIRE command available for Redis? This would act as a sliding expiration value: attempt to get the value specified by the key, and then only if the key was found with this request, set the time to live for X seconds.

No, there isn't, but there's nothing preventing you from sending the two commands one after the other in a MULTI/EXEC block or using a Lua script. Using EXPIRE on a non-existent key does nothing.

Or, I use simple Lua script:
local val, err = redis.pcall('GET', KEYS[1])
if err then
return err
end
redis.call('EXPIRE', KEYS[1], ARGV[1])
return {val}
In Golang you can do:
import "github.com/go-redis/redis"
const lua = `
local val, err = redis.pcall('GET', KEYS[1])
if err then
return err
end
redis.call('EXPIRE', KEYS[1], ARGV[1])
return {val}
`
redisGetEx = redis.NewScript(lua)
result, err = redisGetEx.Run(redisClient, []string{"key"}, 1800).Result()

Related

How to call a Redis Function with StackExchange.Redis

I wrote and register this Redis Function:
local function stincr(KEYS, ARGS)
local value = redis.pcall('INCR', KEYS[1])
if value == 1 or value % 100 == 0 then
redis.call('ZADD', KEYS[2],'GT', tostring(value), KEYS[1])
end
return value;
end
redis.register_function('stincr', stincr)
Redis Functions are introduced in Redis 7. How can I call it with StackExchange.Redis?
As of right now StackExchange.Redis doesn't have any higher-level API wrapping up the functions API, however, you can just use the ad-hoc command API pretty easily. I modified your script to add the shebang in the beginning called for by redis and added it to script.lua:
#!lua name=mylib
local function stincr(KEYS, ARGS)
local value = redis.pcall('INCR', KEYS[1])
if value == 1 or value % 100 == 0 then
redis.call('ZADD', KEYS[2],'GT', tostring(value), KEYS[1])
end
return value;
end
redis.register_function('stincr', stincr)
Then loading/calling the function was pretty straight forward:
var script = File.ReadAllText("script.lua");
var muxer = ConnectionMultiplexer.Connect("localhost");
var db = muxer.GetDatabase();
db.Execute("FUNCTION", "LOAD", script);
var res = db.Execute("FCALL", "stincr", 2, "myNum", "myzset");

use redis incr command after use get command,it return false

when I use the incr and expire command in a pipeline,after I use the get command,it return false,but the key in the redis is already 1.
$pipe = $cur_redis->multi(Redis::PIPELINE);
$pipe->incr($ref_times_key);
$pipe->expire($ref_times_key, $this->getNextRefTimestamp() - time());
$rst=$pipe->exec();

Error in Lua script since last Redis update

Since Redis 6.2.7 (and Redis 7) an existing Lua script stopped working with the error message:
"ERR user_script:6: Attempt to modify a readonly table script: 2f405679dab26da46ec86d29bded48f66a99ff64, on #user_script:6."
The script is working fine with Redis 6.2.6. I did not find any breaking changes in the last Redis release notes.
Any clue ? Thanks !!
Here's the script:
-- returns valid task ID if successfull, nil if no tasks
local tenantId = unpack(ARGV)
local activeTenantsSet, activeTenantsList, tenantQueue = unpack(KEYS)
-- next task lua based function - return nil or taskId
function next ()
local task = redis.call('ZPOPMAX', tenantQueue)
if table.getn(task) == 0 then
redis.call('SREM', activeTenantsSet, tenantId)
redis.call('LREM', activeTenantsList, 0, tenantId)
return nil
end
redis.call('SADD', activeTenantsSet, tenantId)
redis.call('RPUSH', activeTenantsList, tenantId)
return task[1]
end
-------------
return next()
try adding 'local' in front of 'function next'
local function next ()
...
There's a fix for this in a new version of Sentry (the patch also applies cleanly to the lua scripts going back to some older versions):
https://github.com/getsentry/sentry/pull/34416/commits/7c57fe7b17f613fecc47a56c22fff3a20a958496

is it possible to set expire time when using opsForValue to increment value in spring boot

Now I am using this code to increment a value in spring boot :
String loginFailedKey = "admin-login-failed:" + request.getPhone();
Object loginFailedCount = loginFailedTemplate.opsForValue().get(loginFailedKey);
if (loginFailedCount != null && Integer.valueOf(loginFailedCount.toString()) > 3) {
throw PostException.REACH_MAX_RETRIES_EXCEPTION;
}
List<Users> users = userService.list(request);
if (CollectionUtils.isEmpty(users)) {
loginFailedTemplate.opsForValue().increment(loginFailedKey, 1);
throw PostException.LOGIN_INFO_NOT_MATCH_EXCEPTION;
}
is it possible to set an expire time when increment the key? If a new increment command happen, update the expire time. I read the docs and did not found the implement.
There is no direct way in Spring Boot.
One of the indirect ways is to use LUA srcipt.
For example:
RedisScript script = RedisScript.of(
"local i = redis.call('INCRBY', KEYS[1], ARGV[1])"
+ " redis.call('EXPIRE', KEYS[1], ARGV[2])"
+ " return i");
redisTemplate.execute(script, key, String.valueOf(increment),
String.valueOf(expiration));

Tarantool broadcast call

I have cluster with several replicasets. I want to call some stored function on all nodes without calculate bucket_id, and after to map results. How should I do it?
You can use module cartridge.rpc function get_candidates for getting all nodes with some role, which you want to call and after to use module cartridge.pool function map_call for calling your function and mapping results. This function available from 1.2.0-17 version of cartridge. So your code could be like this:
local cartridge = require('cartridge')
local nodes = cartridge.rpc_get_candidates('my_role_name', { leaders_only = true, healthy_only = true })
local pool = require('cartridge.pool')
local results, err = pool.map_call('_G.my_function_name', { func_args }, { uri_list = nodes, timeout = 10 })
if (err ~= nil) then
#your error handling#
end
All function response will be saved to results variable and mapped for every URI. All errors will be saved to err variable as map with keys: line, class_name, err, file, suberrors, str
Another proposal.
If you use vshard and want to perform map-reduce over storages:
local replicaset, err = vshard.router.routeall()
for _, replica in pairs(replicaset) do
local _, err = replica:callrw('function', { args })
if err ~= nil then
return nil, err
end
end