0

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.

Shiva
  • 1,962
  • 2
  • 13
  • 31
  • Redis Lua implementation is sandboxed. - There is simply no ```print()```. - Also no ```io``` and ```os``` library. - It looks that ```print()``` is only used to put out some Info and can be commented/deleted in the code ;-) - Important is what the script do with ```redis.call()``` and maybe what it return. – koyaanisqatsi Oct 23 '22 at 18:47
  • @koyaanisqatsi I wanted to `print` few things for debugging like outcome of the execution of `redis.call` and the script was executing was failing and hence wanted `print` traces to understand where is it failing. – Shiva Oct 23 '22 at 18:58
  • Than use the return() if not really needed. - Example:```eval 'return "Debug: ".._VERSION;' 0``` - Collect Data in a string and return the string. – koyaanisqatsi Oct 23 '22 at 19:02
  • Does this answer your question? [Redis Lua script error: Script attempted to access nonexistent global variable 'print' script](https://stackoverflow.com/questions/74014510/redis-lua-script-error-script-attempted-to-access-nonexistent-global-variable) – for_stack Oct 24 '22 at 02:15
  • @for_stack, error is same however i am running redis on docker – Shiva Oct 29 '22 at 15:42
  • @Shiva It doesn't matter how to run Redis. In whatever cases, Redis doesn't allow most global variables, e.g. `print`. – for_stack Oct 30 '22 at 01:22

1 Answers1

1

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
koyaanisqatsi
  • 2,585
  • 2
  • 8
  • 15