Search code examples
javaluaspring-data-redislettuce

Executing Lua Script with RedisTemplate never works


I am currently working on a benchmark tool for kafka and GC PubSub. I want to see results about min. max and avg transfer speed. In before, i was setting the every entry to reddis hash. After that i was mapping reddis hashes to java map and iterating on them in end of the process for min, max values. It seems so slow because if i call 100000 entry, program iterates x3 for min, max and avg. So i tried to do that with a Lua script. After publishing a message i set a start time to hash map and when listener gets message i take that messages start time from hash and i calculate difference with System.currentTimeMillis. After this step i am trying to execute Lua Script that compares the current value with older value and sets it. But when i execute the script it seems program stops there. I event tried just returning true from Lua script but i couldn't get any response.

 private void calculateSetANDLogAgain(User user){
        long startTime = ((long) redisTemplate.opsForHash().get("times", user.getId()));
        logger.info("Received message -> " + user.toString());
        long duration = 0L;
        duration = System.currentTimeMillis() - startTime;
        Object[] args = new Object[1];
        args[0] = duration;
        System.out.println("BEFORE");
        boolean a = redisTemplate.execute(statisticScript, Collections.singletonList("a"),args);
        System.out.println("AFTER: " + a);
    }

Here i see BEFORE but I cant see AFTER printing. Here is the log output and configuration about script executing. And here is my class tree. Note that RedisConfig class has the bean below.

 @Bean
    public DefaultRedisScript<Boolean> redisscript(){
        DefaultRedisScript defaultRedisScript = new DefaultRedisScript<>();
        defaultRedisScript.setLocation(new ClassPathResource("statistics.lua"));
        defaultRedisScript.setResultType(Boolean.class);
        return defaultRedisScript;
    }

And i am autowiring DefaultRedisScript instance in the class that owns calculateSetANDLogAgain method. Script file just have "return true;"

edit; here is my first script if it helps.

local difference = tonumber(ARGV[1])
local max = tonumber(redis.call("GET","max"))
local min = tonumber(redis.call("GET","min"))
if max == nil then
    redis.call("SET","max",difference)
elseif difference > max then
    redis.call("SET","max",difference)
end
if min == nil then
    redis.call("SET","min",difference)
elseif difference < min then
    redis.call("SET","min",difference)
end

So what may go wrong? I couldn't figured it out...


Solution

  • I solved the problem. It is bizarre but it seems source of the failure is the wrong classpath. I made some changes in my code so take a look.

    Here is the Bean definition.

       @Bean
        public DefaultRedisScript<Boolean> redisscript() {
            DefaultRedisScript defaultRedisScript = new DefaultRedisScript<Boolean>();
            defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("statistics.lua")));
            defaultRedisScript.setResultType(Boolean.class);
            return defaultRedisScript;
        }
    

    Here is the calculateSetANDLogAgain function.

        private void calculateSetANDLogAgain(User user) {
            long startTime = ((long) redisTemplate.opsForHash().get("times", user.getId()));
            logger.info("Received message -> " + user.toString());
            Long duration = null;
            duration = System.currentTimeMillis() - startTime;
            Object[] args = new Object[1];
            args[0] = duration;
            try {
                redisTemplate.execute(statisticScript, null, args);
            } catch (Exception e) {
                finalLogger.info("Error while executing script -> " + e.getLocalizedMessage());
            }
        }
    

    Here is the script;

    local difference = tonumber(ARGV[1])
    local max = tonumber(redis.call("GET","max"))
    local min = tonumber(redis.call("GET","min"))
    if max == nil then
        redis.call("SET","max",tostring(difference));
    elseif difference > max then
        redis.call("SET","max",tostring(difference));
    end
    
    if min == nil then
        redis.call("SET","min",tostring(difference));
    elseif difference < min then
        redis.call("SET","min",tostring(difference));
    end
    return nil;
    

    Finally my script in resources folder and i injected the redistemplate in constructor.