Lua Scripting with redis.Script
Redis allows for the execution of server-side Lua scripts, which is powerful for creating custom atomic operations. Redigo provides the redis.Script
helper type to make working with these scripts simple and efficient.
The Script
Helper
A key challenge with Lua scripting in Redis is managing the script cache. Redis caches compiled scripts and allows you to execute them via their SHA1 hash (EVALSHA
) to save bandwidth. The redis.Script
helper automates this process.
Creating a Script
You create a Script
object with redis.NewScript
, providing the number of keys the script expects and its source code.
var zpopScript = redis.NewScript(1, `
local r = redis.call('ZRANGE', KEYS[1], 0, 0)
if r ~= nil then
r = r[1]
redis.call('ZREM', KEYS[1], r)
end
return r
`)
keyCount
(the first argument): This tells Redigo how many of the script's arguments are key names. Redis uses this to enable features like Redis Cluster. If you don't know the number of keys in advance, you can use-1
, but you will have to provide the key count yourself when executing the script.src
(the second argument): The Lua script source code as a string.
The NewScript
function automatically calculates the SHA1 hash of the script for you.
Executing a Script
The script.Do()
method is the primary way to execute a script. It implements an optimistic execution strategy:
- It first attempts to execute the script using
EVALSHA
with its cached SHA1 hash. - If the server responds with a
NOSCRIPT
error (meaning the script is not in its cache), Redigo automatically resends the command usingEVAL
, which sends the full script source. The server then executes and caches the script. - Subsequent calls to
Do()
for the same script on the same server will succeed on the firstEVALSHA
attempt.
This pattern provides the performance benefits of EVALSHA
without the manual burden of checking if the script is loaded.
// zpopScript defined as above
c, err := redis.Dial("tcp", ":6379")
// handle err
defer c.Close()
// Add some data to a sorted set
c.Do("ZADD", "myzset", 1, "one", 2, "two", 3, "three")
// Execute the script. Redigo handles EVALSHA/EVAL logic.
// The arguments to Do are the connection, followed by the script's KEYS and ARGV.
member, err := redis.String(zpopScript.Do(c, "myzset"))
if err != nil {
// handle error
}
fmt.Println("Popped member:", member) // Popped member: one
Other Script
Methods
-
s.Load(c Conn)
: Explicitly loads the script into the Redis cache usingSCRIPT LOAD
. This is useful if you want to ensure a script is available before a critical, time-sensitive operation. -
s.Send(c Conn, keysAndArgs ...interface{})
: Sends anEVAL
command for the script but does not wait for the reply (for use in pipelining). -
s.SendHash(c Conn, keysAndArgs ...interface{})
: Sends anEVALSHA
command for the script but does not wait for the reply. Use this only when you are certain the script is already loaded to avoidNOSCRIPT
errors.