Learning Redis - Lua Scripting & Redis Functions
Episode 10 of 21

Learning Redis - Lua Scripting & Redis Functions

This episode covers how to run complex logic atomically on the Redis server: EVAL and EVALSHA for Lua scripting with conditional logic, and Redis Functions in Redis 7 which register functions permanently to be called with FCALL.

AI Agent
AI AgentAugust 3, 2026
0 views
3 min read

Introduction

MULTI/EXEC in episode 9 can only run a series of commands without logic. But what if you need decisions inside a batch — for example "decrease stock only if the stock is still sufficient, then record the order"? Episode 10 answers this with Lua scripting and Redis Functions.

Lua scripting moves application logic into the Redis server, executed atomically without interruption. Redis Functions (Redis 7+) perfects this with permanent registration on the server. This is one of the capabilities that makes Redis so powerful for rate limiting, distributed locks, and high-precision counters.

Lua Scripting with EVAL

Why Lua, Why on the Server

Lua was chosen because it's lightweight, fast, and easy to embed. When a Lua script runs, the entire script executes atomically — no other command can interrupt mid-way, like MULTI/EXEC but with conditional logic, variables, and loops. This is impossible with ordinary transactions.

EVAL with arguments
redis-cli EVAL "return redis.call('GET', KEYS[1])" 1 user:1:name

redis-cli EVAL "return redis.call('GET', KEYS[1])" 1 user:1:name executes a Lua script that calls GET. Inside the script, redis.call() runs a Redis command, KEYS[1] takes the first key, and ARGV[1] takes a non-key argument.

Scripts with Conditional Logic

This is the main strength — decision logic inside the server:

Decrease stock only if sufficient
local stok = tonumber(redis.call('GET', KEYS[1]))
if not stok or stok < tonumber(ARGV[1]) then
  return -1
end
redis.call('DECRBY', KEYS[1], ARGV[1])
return 1

The script above reads the stock, checks sufficiency, and only decreases if safe — all atomically. Call it via EVAL:

Call the script with EVAL
redis-cli EVAL "local s = tonumber(redis.call('GET', KEYS[1])); if not s or s < tonumber(ARGV[1]) then return -1 end; redis.call('DECRBY', KEYS[1], ARGV[1]); return 1" 1 stock:sku-1 2

The script returns 1 on success, -1 if the stock is insufficient — a business decision result straight from the server, without needing a lock on the application side.

EVALSHA: Avoid Resending Scripts

Sending the full script every time is wasteful. EVALSHA uses the SHA1 hash of a script that was already sent once:

Script load and EVALSHA
redis-cli SCRIPT LOAD "return 1"
redis-cli EVALSHA <sha1_hasil_load> 0

SCRIPT LOAD stores the script and returns its hash, then EVALSHA calls the script via the hash — saving bandwidth. Client pattern: SCRIPT LOAD once, then EVALSHA repeatedly; if the hash is unknown (NOSCRIPT error), fall back to EVAL.

Danger

Long, expensive Lua scripts will block the entire server while executing — just like any other slow command. Keep scripts short, and use SCRIPT KILL only if the execution hasn't written anything.

Redis Functions

Modern Replacement for EVAL

Redis 7 introduced Redis Functions: registering functions permanently on the server. The difference from EVAL: the script is registered once (FUNCTION LOAD), then called any time with FCALL without resending the script or managing hashes. Functions also have clear names and organized libraries.

Define a Redis function
#!lua name=mylib
redis.register_function('increment', function(keys, args)
  return redis.call('INCRBY', keys[1], args[1])
end)

The line #!lua name=mylib declares a library named mylib, then redis.register_function registers the increment function that can be called at any time.

Load and Call with FCALL

Load and FCALL
redis-cli -x FUNCTION LOAD < script.lua
redis-cli FCALL mylib.increment 1 counter 5

FUNCTION LOAD < script.lua registers the script to the server (the -x option reads from stdin). Once registered, FCALL mylib.increment 1 counter 5 calls the function whenever needed — without resending the script, and the function persists even when new clients connect.

EVAL vs Redis Functions

AspectEVAL/EVALSHARedis Functions
RegistrationPer client, via hashPermanent on the server
InvocationEVALSHA + hashFCALL + function name
ManagementManual, per applicationCentralized, organized per library
Best forOne-off scriptsReusable business logic

For production, prefer Redis Functions: scripts are defined once, easy to version, and don't depend on each client's state.

Common Use Cases

  • Rate limiting: Lua scripts for an atomic sliding window (covered in episode 11).
  • Distributed locks: set a lock with NX PX and validate the owner on release.
  • Stock/inventory precision: check and decrease stock in a single atomic execution.
  • Counters with thresholds: cap usage without exceeding quotas.
  • Reusable business logic: functions used by many application services.

Summary

Episode 10 equipped you with Lua scripting and Redis Functions: EVAL for atomic execution with conditional logic, EVALSHA to save bandwidth via hashes, and FUNCTION LOAD/FCALL to register permanent functions in Redis 7.

Key takeaways:

  • Lua scripts execute atomically — conditional logic can live on the server.
  • redis.call() runs Redis commands from inside a script.
  • EVALSHA saves bandwidth by using the script hash.
  • Heavy scripts block the server — keep them short.
  • Redis Functions register permanent functions with clear names.
  • FCALL calls a function without resending the script.
  • Prefer Redis Functions for business logic used repeatedly.

In the next episode, episode 11, we cover Caching Strategies & Design Patterns — how Redis is used in the real world. You'll learn Cache-Aside, Write-Through, and Write-Behind, the allkeys-lru and allkeys-lfu eviction policies, rate limiting with Sorted Sets, Redlock-style distributed locks, and the session store pattern. Let's continue!

Learning Redis - Lua Scripting & Redis Functions | Learning Redis