Topic 3.4
Redis Functions: Server-Side Logic as a Deployed Artifact
In one line
Redis Functions (7.0+) are named Lua functions grouped into libraries, loaded once with FUNCTION LOAD, called with FCALL, and persisted and replicated like data. They fix the script-cache problems of EVALSHA and give server-side logic a versioned deployment lifecycle.
Think of it like this
The difference between emailing a recipe to the kitchen every time you want a dish (EVAL) and adding it to the kitchen's official recipe book (FUNCTION LOAD). Once it's in the book, cooks can make it by name, new kitchens get a copy of the book, and you update it deliberately with a new edition.
Key ideas
- 01
A library is Lua code that starts with a shebang naming the engine and library:
#!lua name=ratelimit, and registers functions withredis.register_function('allow', function(keys, args) ... end)or the table form withflags(for exampleno-writesfor read-only functions, callable withFCALL_RO). - 02
Lifecycle commands:
FUNCTION LOAD [REPLACE] code,FUNCTION LIST [WITHCODE],FUNCTION DELETE lib,FUNCTION DUMP/FUNCTION RESTORE(export and import all libraries),FUNCTION FLUSH,FUNCTION STATS, andFUNCTION KILL(for a running read-only function). - 03
Functions are part of the dataset: saved in RDB and AOF, replicated to replicas, and present after a restart or failover. No more
NOSCRIPTsurprises, and every client calls the same version by name instead of each app shipping its own copy of a script. - 04
Execution is identical to Lua scripts: atomic, blocking while running, same
busy-reply-threshold, same cluster rule that all keys must share a slot. In Redis Cluster, functions must be loaded on every primary (replicas get them via replication); tooling likeredis-cli --cluster callhelps. - 05
Versioning strategy: include a version in the library or function name (
ratelimit_v2.allow), deploy the new version alongside the old one, switch clients over, then delete the old library. That mirrors how you'd roll out a database migration. - 06
Use scripts (EVAL) for logic owned by one application and shipped with it; use Functions when logic is shared by several services, needs to survive restarts without client reloads, or is managed by a platform team.
Code & diagrams
#!lua name=ratelimit_v1
local function allow(keys, args)
local limit, window = tonumber(args[1]), tonumber(args[2])
local current = redis.call('INCR', keys[1])
if current == 1 then redis.call('EXPIRE', keys[1], window) end
if current > limit then return 0 end
return 1
end
local function remaining(keys, args)
local used = tonumber(redis.call('GET', keys[1]) or '0')
return math.max(0, tonumber(args[1]) - used)
end
redis.register_function('rl_allow', allow)
redis.register_function{
function_name = 'rl_remaining',
callback = remaining,
flags = { 'no-writes' } -- callable with FCALL_RO, even on replicas
}# Load (or replace) the library
cat ratelimit-lib.lua | redis-cli -x FUNCTION LOAD REPLACE
"ratelimit_v1"
redis-cli FCALL rl_allow 1 rl:{user:42}:28792000 100 60
(integer) 1
redis-cli FCALL_RO rl_remaining 1 rl:{user:42}:28792000 100
(integer) 99
redis-cli FUNCTION LIST LIBRARYNAME ratelimit
1) 1) "library_name" 2) "ratelimit_v1" 3) "engine" 4) "LUA"
5) "functions" ...
# Load on every primary of a cluster
redis-cli --cluster call 10.0.0.1:6379 FUNCTION LOAD REPLACE "$(cat ratelimit-lib.lua)"
# Back up and restore all libraries
redis-cli --no-raw FUNCTION DUMP > functions.dumpInterview problem
The problem
Rolling out shared server-side logic
Five services use the same rate-limiting Lua script, each shipping its own copy. After a Redis failover, some services start failing with NOSCRIPT, and two copies of the script have drifted apart. Propose how to fix this with Functions and how to deploy a new version safely.
The interviewer follows up
What happens to functions when you add a new shard to a cluster?
Explain it without notes
How do Functions differ from EVAL scripts in lifecycle and persistence?
What does the no-writes flag allow?
Practice
Convert the inventory purchase script from Topic 3.2 into a Functions library with buy and a read-only stock_left.
Trade-offs
- ↔
Functions give durable, shared, versioned logic but add a deployment step and an admin permission to manage.
- ↔
Like scripts, functions block the server while running; they aren't a place for heavy computation.
Done when you can
I can write, load, call, list and delete a function library.
I can deploy a new function version without downtime.
I know when to choose Functions over EVAL.