Designing a Distributed Rate Limiter with Redis and Lua Scripts
A deep dive into sub-millisecond sliding window counter algorithms, token buckets, and coordinating distributed rate limiting across multi-region API gateways.
System Design: From Zero to Production
A comprehensive engineering series guiding backend developers from single-node instances to highly resilient distributed architectures.
Introduction
Rate limiting is critical for protecting upstream services, preventing cascading failures, and enforcing API tier quotas. When scaling API gateways handling hundreds of thousands of concurrent requests, in-memory rate limiting on individual instances falls short because traffic is load-balanced across multiple nodes.
Coordinating rate limit counters across horizontally scaled NestJS instances with sub-millisecond overhead using Redis and atomic Lua scripts.
Architecture
sequenceDiagram
autonumber
Client->>Gateway: HTTP GET /api/v1/resource
Gateway->>Redis: EVALSHA sliding_window.lua (Key, Limit, Window)
Redis-->>Gateway: [Allowed: 1, Remaining: 42, Reset: 15000]
Gateway->>Backend: Forward Request
Backend-->>Client: HTTP 200 OK
Ensure all Redis nodes and API gateway instances synchronize via NTP. Time drift beyond 50ms will distort sliding window bucket calculation.
Benchmark Results
Sliding Window vs Token Bucket (1M ops/sec)
Implementation Code
export async function acquireRateLimit(key: string, limit: number, windowMs: number): Promise<boolean> {
const now = Date.now();
const clearBefore = now - windowMs;
const result = await redis.eval(
SLIDING_WINDOW_SCRIPT,
1,
key,
now,
clearBefore,
limit
);
return result === 1;
}
Zero-Downtime PostgreSQL Schema Migrations at Scale
Alex Rivera
@alexdev
Principal Distributed Systems Engineer. Specializing in high-throughput caching and message brokers.