In this lesson, you will learn how to synchronize rate-limiting logic across a cluster of Node.js nodes using a shared Redis "Leaky Bucket" to ensure consistent throughput regardless of which server handles a request.
The Problem: Local vs. Distributed State
When you run multiple instances of a WebSocket server, keeping rate limits in local memory leads to "drift." If a user sends 10 messages, and your limit is 5 per second, they could accidentally bypass this by hitting different server nodes that don't know about each other’s counters. By moving the "leaky bucket" state to Redis, we create a single, shared source of truth.
How the Leaky Bucket Works in Redis
Imagine a bucket with a hole in the bottom. Every time a message arrives, we try to add "water" (a unit of cost). The hole represents the constant rate at which the bucket drains. If the water overflows, the request is rejected.
We can implement this atomic logic using a Lua script in Redis. This ensures that checking the bucket and updating the count happens as a single, uninterruptible operation, preventing race conditions between your Node.js processes.
const luaScript = `
local bucket_key = KEYS[1]
local capacity = tonumber(ARGV[1])
local leak_rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local bucket = redis.call('HMGET', bucket_key, 'last_level', 'last_time')
local level = math.max(0, (tonumber(bucket[1]) or 0) - (now - (tonumber(bucket[2]) or now)) * leak_rate)
if level + 1 <= capacity then
redis.call('HMSET', bucket_key, 'last_level', level + 1, 'last_time', now)
return 1 -- Allowed
else
return 0 -- Rate limited
end
`;
Why This Scales
By executing this script via redis.eval(), the state is persisted in Redis memory. When a WebSocket message hits Node-A, it updates the shared Redis key. When the next message arrives at Node-B, the Lua script pulls the current last_level and last_time from Redis, calculates the "leak" that occurred since the last request, and applies the logic correctly. This guarantees that your cluster enforces a global throughput limit across all connected clients.
Key Takeaway: By moving rate-limit state into an atomic Redis Lua script, you transform localized constraints into a reliable, cluster-wide policy that prevents throughput spikes from overwhelming your infrastructure.