Go Redis Lua Scripting


go-redis supports Lua scripting with redis.Scriptopen in new window, for exampleopen in new window, the following script implements INCRBY command in Lua using GET and SET commands:

var incrBy = redis.NewScript(`
local key = KEYS[1]
local change = ARGV[1]

local value = redis.call("GET", key)
if not value then
  value = 0

value = value + change
redis.call("SET", key, value)

return value

You can then run the script like this:

keys := []string{"my_counter"}
values := []interface{}{+1}
num, err := incrBy.Run(ctx, rdb, keys, values...).Int()

Internally, go-redis uses EVALSHAopen in new window to execute the script and fallbacks to EVALopen in new window if the script does not exist.

You can find the example above at GitHubopen in new window. For a more realistic example, check redis_rateopen in new window which implements a leacky bucket rate-limiter.

Lua and Go types

Underneath, Lua's number type is a float64 number that is used to store both ints and floats. Because Lua does not distinguish ints and floats, Redis always converts Lua numbers into ints discarding the decimal part, for example, 3.14 becomes 3. If you want to return a float value, return it as a string and parse the string in Go using Float64open in new window helper.

Lua returnGo interface{}
number (float64)int64 (decimal part is discarded)
falseredis.Nil error
{ok = "status"}string("status")
{err = "error message"}errors.New("error message")
{"foo", "bar"}[]interface{}{"foo", "bar"}
{foo = "bar", bar = "baz"}[]interface{}{} (not supported)

Debugging Lua scripts

The easiest way to debug your Lua scripts is using redis.log function that writes the message to the Redis log file or redis-server output:

redis.log(redis.LOG_NOTICE, "key", key, "change", change)

If you prefer a debugger, check Redis Lua scripts debuggeropen in new window.

Passing multiple values

You can use for loop in Lua to iterate over the passed values, for example, to sum the numbers:

local key = KEYS[1]

local sum = redis.call("GET", key)
if not sum then
  sum = 0

local num_arg = #ARGV
for i = 1, num_arg do
  sum = sum + ARGV[i]

redis.call("SET", key, sum)

return sum

The result:

sum, err := sum.Run(ctx, rdb, []string{"my_sum"}, 1, 2, 3).Int()
fmt.Println(sum, err)
// Output: 6 nil

Loop continue

Lua does not support continue statements in loops, but you can emulate it with a nested repeat loop and a break statement:

local num_arg = #ARGV

for i = 1, num_arg do

  if true then
    do break end -- continue

until true

Error handling

By default, redis.call function raises a Lua error and stops program execution. If you want to handle errors, use redis.pcall function which returns a Lua table with err field:

local result = redis.pcall("rename", "foo", "bar")
if type(result) == 'table' and result.err then
  redis.log(redis.LOG_NOTICE, "rename failed", result.err)

To return a custom error, use a Lua table:

return {err = "error message goes here"}

Monitoring Performance

go-redis comes with a plugin that allows to monitor performance and pinpoint bottlenecks using OpenTelemetry Tracing and Metrics.

See also

The following guides can help you get started with Lua scripting in Redis: