Search code examples
dockerredisluaredis-cli

print method in lua script/redis is not working


I am trying to execute the following script and getting the below error. Redis is running in docker

Exception in thread "main" org.redisson.client.RedisException: ERR user_script:1: Script attempted to access nonexistent global variable 'print' script: 6f736423f082e141036b833d1f86b5a36a494611, on @user_script:1..

I get the same error when I execute using redis CLI

127.0.0.1:6379> eval "print("Comparison is_made b/w minimum_value out of two is: ")" 0 (error) ERR user_script:1: Script attempted to access nonexistent global variable 'print' script: 8598b7f0db450c711d3a9e73a296e331bd1ef945, on @user_script:1. 127.0.0.1:6379>

Java code. I am using Redison lib to connect to Redis and execute script.

 String script = "local rate = redis.call('hget', KEYS[1], 'rate');"
                + "local interval = redis.call('hget', KEYS[1], 'interval');"
                + "local type = redis.call('hget', KEYS[1], 'type');"
                + "assert(rate ~= false and interval ~= false and type ~= false, 'RateLimiter is not initialized')"

                + "local valueName = KEYS[2];"
                + "local permitsName = KEYS[4];"
                + "if type == '1' then "
                + "valueName = KEYS[3];"
                + "permitsName = KEYS[5];"
                + "end;"
                +"print(\"rate\"..rate) ;"
                +"print(\"interval\"..interval) ;"
                +"print(\"type\"..type); "
                + "assert(tonumber(rate) >= tonumber(ARGV[1]), 'Requested permits amount could not exceed defined rate'); "

                + "local currentValue = redis.call('get', valueName); "
                + "local res;"
                + "if currentValue ~= false then "
                + "local expiredValues = redis.call('zrangebyscore', permitsName, 0, tonumber(ARGV[2]) - interval); "
                + "local released = 0; "
                + "for i, v in ipairs(expiredValues) do "
                + "local random, permits = struct.unpack('Bc0I', v);"
                + "released = released + permits;"
                + "end; "

                + "if released > 0 then "
                + "redis.call('zremrangebyscore', permitsName, 0, tonumber(ARGV[2]) - interval); "
                + "if tonumber(currentValue) + released > tonumber(rate) then "
                + "currentValue = tonumber(rate) - redis.call('zcard', permitsName); "
                + "else "
                + "currentValue = tonumber(currentValue) + released; "
                + "end; "
                + "redis.call('set', valueName, currentValue);"
                + "end;"

                + "if tonumber(currentValue) < tonumber(ARGV[1]) then "
                + "local firstValue = redis.call('zrange', permitsName, 0, 0, 'withscores'); "
                + "res = 3 + interval - (tonumber(ARGV[2]) - tonumber(firstValue[2]));"
                + "else "
                + "redis.call('zadd', permitsName, ARGV[2], struct.pack('Bc0I', string.len(ARGV[3]), ARGV[3], ARGV[1])); "
                + "redis.call('decrby', valueName, ARGV[1]); "
                + "res = nil; "
                + "end; "
                + "else "
                + "redis.call('set', valueName, rate); "
                + "redis.call('zadd', permitsName, ARGV[2], struct.pack('Bc0I', string.len(ARGV[3]), ARGV[3], ARGV[1])); "
                + "redis.call('decrby', valueName, ARGV[1]); "
                + "res = nil; "
                + "end;"

                + "local ttl = redis.call('pttl', KEYS[1]); "
                + "if ttl > 0 then "
                + "redis.call('pexpire', valueName, ttl); "
                + "redis.call('pexpire', permitsName, ttl); "
                + "end; "
                + "return res;";

        RedissonClient client = null;
        try {
             client = Redisson.create();
            client.getRateLimiter("user1:endpoint1").setRate(
                    RateType.PER_CLIENT, 5, 1, RateIntervalUnit.SECONDS);
            String sha1 = client.getScript().scriptLoad(script);
            List<Object> keys =
                    Arrays.asList("user1:endpoint1", "{user1:endpoint1}:value",
                            "{user1:endpoint1}:value:febbb04d-6365-4cb8-b32b-8d90800cd4e6",
                            "{user1:endpoint1}:permits", "{user1:endpoint1}:permits:febbb04d-6365-4cb8-b32b-8d90800cd4e6");
            byte[] random = new byte[8];
            ThreadLocalRandom.current().nextBytes(random);
            Object args[] = {1, System.currentTimeMillis(), random};
            boolean res = client.getScript().evalSha(READ_WRITE, sha1, RScript.ReturnType.BOOLEAN, keys, 1,
                    System.currentTimeMillis(), random);

            System.out.println(res);
        }finally {
            if(client != null && !client.isShutdown()){
                client.shutdown();
            }
        }

checked the Lua print on the same line thread but io.write also is giving same error.


Solution

  • As in the comments wrote return() seems* the only way.
    Example for redis-cli (set redis DB and use it in Lua)
    (Collect Data and return as one string)

    set LuaV 'local txt = "" for k, v in pairs(redis) do txt = txt .. tostring(k) .. " => " .. tostring(v) .. " | " end return(txt)'
    

    Now the eval

    eval "local f = redis.call('GET', KEYS[1]) return(loadstring(f))()" 1 LuaV
    

    ...shows whats in table: redis
    (One long String no \n possible)

    • Exception: eval 'redis.log(2, _VERSION)' 0 gives out without ending the script but only on the server.
      Than \n will work when you do...
    set LuaV 'local txt = "" for k, v in pairs(redis) do txt = txt .. tostring(k) .. " => " .. tostring(v) .. "\n" end return(txt)' 
    

    ...and the eval

    eval 'local f = redis.call("GET", KEYS[1]) f = loadstring(f)() redis.log(2, f)' 1 LuaV