15
votes

I am building out my first Redis server side script (for debugging) and my lack of Lua experience has me quite stuck.

Essentially have a dataset of K/V pairs (containing ~1000 values) from which I want to list all the KEYS that match a pattern. For example in redis-cli:

> KEYS "carlos:*"
1) "carlos:1"
2) "carlos:2"
3) "carlos:3"
4) "carlos:4"

Based on the above output I want to return the sum of those keys by executing a Lua script. Currently I have the following on my sum.lua

local sum = 0
local matches = redis.call('KEYS', 'carlos:*')

for unpack(matches)
   sum = sum + redis.call('GET', matches)
end

return sum

While the above script is likely incorrect, trying even redis.call('KEYS', 'carlos:*') by itself produces the following error

root@carlos:~# redis-cli EVAL "$(cat sum.lua)"

(error) ERR wrong number of arguments for 'eval' command

I have tried a number of iterations of my syntax to no avail. Any ideas?

Thanks

2

2 Answers

23
votes
  1. EVAL requires a minimum of two arguments; the script and the number of keys you are passing to the script. In this case, you are passing zero keys, meaning the script can be invoked as follows:

    redis-cli EVAL "$(cat sum.lua)" 0
    

    or:

    redis-cli --eval sum.lua
    
  2. Your loop structure for iterating over the values returned from KEYS was incorrect; I have fixed it for you.

  3. You need to convert the value returned from GET from a string to a number using Lua's tonumber function.

With the above changes made, the following script should work for you:

local sum = 0
local matches = redis.call('KEYS', 'carlos:*')

for _,key in ipairs(matches) do
    local val = redis.call('GET', key)
    sum = sum + tonumber(val)
end

return sum
2
votes

Some important points for starters:

KEYS and ARGV : These are tables in Lua which are going to hold the data sent by your redis client inside Lua Script in Redis.

  • Tables are associative arrays, and Lua’s only mechanism for structuring data. You can think of them as the equivalent of an array in whatever language you’re most comfortable with.Tables are one-based, that is, indexing starts at 1. So the first element in mytable is mytable[1], the second is mytable[2], etc.

  • Tables cannot hold nil values. If an operation would yield a table of [ 1, nil, 3, 4 ], the result will instead be [ 1 ] — the table is truncated at the first nil value.

Note : When writing Lua scripts for Redis, every key that is accessed should be accessed only by the KEYS table. The ARGV table is used for parameter-passing

I think you can now transfer keys and args from whatever language to your Lua script.

Now we can proceed to Lua's redis call to redis command KEYS.

To iterate over the returned data you have option of lua's pairs and ipairs and some quick difference can be spotted here. For above case ipairs will be fine.

Now we can proceed to data type issue.

Lua and Redis have different type systems, so it’s important to understand how values may change when crossing the Redis-Lua border. When a number comes from Lua back to a Redis client, it becomes an integer — any digits past the decimal point are dropped:

local indiana_pi = 3.2
return indiana_pi

When you run this script, Redis will return an integer of 3 — you lose the interesting pieces of pi. Seems simple enough, but things get a bit more tricky when you start interacting with Redis in the middle of the script. An example:

local indiana_pi = 3.2
redis.call("SET", "pi", indiana_pi)
return redis.call("GET", "pi")

The resulting value here is a string: "3.2" Why? Redis doesn’t have a dedicated numeric type. When we first SET the value, Redis saves it as a string, losing all record of the fact that Lua initially thought of the value as a float. When we pull the value out later, it’s still a string.

Values in Redis that are accessed with GET/SET should be thought of as strings except when numeric operations like INCR and DECR are run against them. These special numeric operations will actually return integer replies (and manipulate the stored value according to mathematical rules), but the “type” of the value stored in Redis is still a string value.

Above answer is taken from this awesome blog post on:

https://www.redisgreen.net/blog/intro-to-lua-for-redis-programmers/