"How would you design a rate limiter for a distributed system?"
Rate limiting is a critical component of any production system, protecting services from abuse, DDoS attacks, and cascading failures. This question tests your knowledge of algorithms, distributed systems, and real-world engineering trade-offs. It appears frequently at ByteDance, Google, Amazon, and other major companies.
Clarify requirements: where to apply (API gateway vs. application layer), rate limiting dimensions (per user, per IP, per API key), rules (100 requests/minute, 1000/hour), response behavior (429 Too Many Requests with Retry-After header).
Present rate limiting algorithms: Token Bucket (smooth burst handling), Leaky Bucket (constant rate output), Fixed Window Counter (simple but has boundary burst issue), Sliding Window Log (accurate but memory-heavy), Sliding Window Counter (hybrid approach).
Design for distributed systems: centralized store (Redis) for consistent counting across multiple servers, with Lua scripts for atomic operations.
Handle edge cases: race conditions (Redis Lua scripts or MULTI/EXEC), clock synchronization, fault tolerance (allow traffic if Redis is down — fail open).
Discuss the response: HTTP 429 status code, X-RateLimit-Remaining and X-RateLimit-Reset headers, client-side retry with exponential backoff.
A rate limiter controls the number of requests a client can make within a time window. I would design it as follows. First, requirements: support per-user and per-API rate limits, handle 10,000+ requests/second, work across a distributed server fleet, and add <5ms latency. I would choose the Token Bucket algorithm for its flexibility. Each user/API key has a bucket with a maximum capacity (burst size) and a refill rate (sustained rate). For example, capacity=10, refill=5/second means a burst of 10 requests, sustained rate of 5/second. When a request arrives, if the bucket has tokens, consume one and allow the request; otherwise, reject with HTTP 429. For distributed rate limiting, I would use Redis as the centralized token store. The check-and-update operation must be atomic to prevent race conditions. I would use a Redis Lua script: local tokens = tonumber(redis.call("get", key) or capacity); local last_refill = tonumber(redis.call("get", key..":time") or now); tokens = min(capacity, tokens + (now - last_refill) * refill_rate); if tokens >= 1 then tokens = tokens - 1; redis.call("setex", key, ttl, tokens); redis.call("setex", key..":time", ttl, now); return 1; else return 0. Lua scripts execute atomically in Redis, eliminating race conditions. Architecture: request → API gateway / middleware → Redis token bucket check → if allowed, forward to application server; if denied, return 429 with headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After). For fault tolerance, if Redis is unavailable, I would fail open (allow requests) with local in-memory rate limiting as a fallback — protecting users is better than blocking everyone. For multiple rate limit rules (100/minute AND 1000/hour), check all rules and allow only if all pass. At massive scale, use Redis Cluster with consistent hashing to distribute keys, and consider local counters with periodic sync for ultra-low latency.
Know at least 3 rate limiting algorithms and their tradeoffs — interviewers will ask you to compare them.
The fixed window boundary burst problem is a common follow-up — be ready to explain and solve it with sliding window.
Mention fail-open vs. fail-closed as a design decision — it shows production thinking.
Use HireMe AI during system design interviews to quickly recall algorithm details and Redis commands.
With a limit of 100 requests/minute, a client can send 100 at 0:59 and 100 at 1:00, effectively sending 200 in 2 seconds. Sliding window counters solve this by weighting the previous window.
Token Bucket allows bursts up to bucket capacity and is more flexible for APIs. Leaky Bucket processes requests at a constant rate, smoothing traffic but not allowing bursts. Token Bucket is more common in practice.
Both. API gateway handles global rate limits (per IP, per API key) for all services. Application-level handles business-specific limits (per user per feature). This defense-in-depth approach provides the best protection.
HireMe AI listens to your interview in real-time, detects questions, and generates instant answer suggestions — so you never freeze up again.
Try HireMe AI Free