patternjavascriptredisModerate
Redis Lua scripting for atomic multi-key operations
Viewed 0 times
redisluaEVALEVALSHAatomicconditionalrace conditionclusterKEYSserver-side script
Error Messages
Problem
Sequences of Redis commands that must be atomic (check-then-act, multi-key updates) are vulnerable to race conditions when written as separate client calls, even inside a MULTI/EXEC block that does not support conditionals.
Solution
Use the Redis EVAL command or EVALSHA with Lua scripts to execute conditional logic atomically on the server. Redis runs Lua scripts single-threaded with no interleaving, providing true atomicity across multiple keys.
Why
MULTI/EXEC is optimistic — it queues commands blindly without branching. Lua scripts can read values and branch conditionally within a single atomic execution on the Redis server side, replacing patterns like GET-then-SET that are inherently racy.
Gotchas
- All keys accessed in a Lua script must be declared in the KEYS array for cluster compatibility
- Lua scripts block the entire Redis server while running — keep them short (less than 1ms)
- Use EVALSHA with pre-loaded scripts in production to avoid resending script text on every call
- redis.call() inside Lua raises an error on Redis command failure; redis.pcall() catches it as a return value
Code Snippets
Atomic increment-if-below-limit using a Redis Lua script via the ioredis client
// luaScript is sent to Redis server and executed atomically there
// redis.sendCommand invokes the EVAL command on the Redis server
const luaScript = `
local current = tonumber(redis.call('GET', KEYS[1])) or 0
if current >= tonumber(ARGV[1]) then
return 0
end
redis.call('INCR', KEYS[1])
redis.call('EXPIRE', KEYS[1], ARGV[2])
return 1
`;
// Sends the Lua script to Redis with 1 key, limit=100, ttl=3600
// Returns 1 if incremented, 0 if rate limit reached
const allowed = await redis.sendCommand([
'EVAL', luaScript, '1', 'ratelimit:user:42', '100', '3600'
]);Revisions (0)
No revisions yet.