CodeWithYash
DSA
Spring Boot
System Design
Series
Articles
Leaderboard
Sign In

Subscribe to Newsletter

Get the latest articles, DSA sheets, and system design challenges delivered to your inbox.

<CODEWITHYASH/>

The ultimate sanctuary for software engineers seeking deep mastery of core technical principles.

Library

  • Articles
  • DSA Sheets
  • Roadmaps

Connect

  • GitHub
  • LinkedIn
  • About
  • Contact

Legal

  • Privacy
  • Terms
  • Support

© 2026 CodeWithYash Global Systems.

Status: OperationalVersion: 4.2.0
System Design
OverviewTheory lessonsHLDLLD
All SeriesSeries
Distributed Systems

System Design Series

0%

0 of 33 topics completed

Progress
Load Balancers & Reverse ProxiesTheory
25 min
API Gateways & Edge RoutingTheory
35 min
Caching Architecture & Critical PathologiesTheory
30 min
Consistent Hashing & Virtual NodesTheory
25 min
Content Delivery Networks (CDNs) & Anycast RoutingTheory
35 min
Storage Engines: B-Trees vs. LSM-TreesTheory
30 min
SQL vs. NoSQL & Data Model SelectionTheory
35 min
Database Scaling: Sharding, Partitioning & ReplicationTheory
30 min
Message Brokers, Event Streaming & Task QueuesTheory
30 min
Rate Limiters & Traffic ShapingTheory
25 min
System DesignCore Infrastructure Building BlocksRate Limiters & Traffic Shaping

Component Architecture · Phase 2: Core Infrastructure

Rate Limiters & Traffic Shaping

Compare rate limiting algorithms (Token Bucket, Leaky Bucket, Fixed Window, Sliding Window Counter), and design a high-throughput distributed limiter using Redis Lua scripts.

Primary specification: Better Rate Limiting with Redis Sorted Sets (Figma Engineering)

Written & reviewed by Yash Patel 25 minBaseline 2026.1 Curated Blueprint· Verified 2026-09-14

Lesson 17 of 33

1d streak

The Architectural Question

Malicious scrapers or misconfigured client retry loops overwhelm backend APIs with millions of requests, causing resource starvation for legitimate users.

Core Engineering Objectives

  • Algorithm trade-offs: Token Bucket vs Leaky Bucket vs Fixed Window vs Sliding Window Counter
  • Distributed enforcement: atomic Redis Lua scripts vs local in-memory token bucket approximations
  • HTTP 429 semantics: `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining` headers

Interactive system trace

Watch the architecture make its key decision

Switch scenarios, predict the next active component, and step through the request or failure path.

Within budget

Rate Limiting

Client yash has 3 tokens and sends one API request.

●●●○○○○○○○

Admitted

1

One token spent atomically per request

Rejected

0

All requests fit the budget

Retry after

Not needed

At the selected refill rate

The bucket caps burst size at ten tokens; refill restores tokens over time, never above capacity.

Adjust the bucket

Identify callerkey = yash

The request is grouped by API key, user, or IP.

Why it matters: Limits need a stable identity and policy scope.

1 / 4
Step 1 of 4

1. What is it & Why it Matters

Every public and internal API is vulnerable to resource exhaustion. Whether from malicious DDoS attacks, credential-stuffing scrapers, runaway customer scripts, or internal services caught in retry storms, unconstrained traffic inevitably saturates database connection pools and takes down infrastructure. Distributed rate limiting enforces admission control at the ingress gateway, protecting downstream systems and guaranteeing tenant fairness.

The Distributed Concurrency Challenge

On a single server, rate limiting is simple: an in-memory counter protected by a mutex lock suffices. In a horizontally scaled cluster of 50 API gateways behind a load balancer, rate limiting becomes a distributed state problem: gateways must synchronize rate-limiting state with sub-millisecond latency without introducing single-point-of-failure bottlenecks or check-then-act race conditions.

2. Algorithmic Foundations: 4 Core Approaches

Token Bucket

Tokens refill at a constant rate R up to maximum burst capacity B. Each request consumes 1 token. Permits bursts up to capacity B while bounding long-term average throughput to R. O(1) memory and computation.

Leaky Bucket

Requests enter a FIFO queue and leak out at a strictly constant rate R. Smooths bursty traffic into a steady output stream. Rejects requests when the queue overflows.

Sliding Window Log

Maintains exact timestamps for every request in a sorted set over window [now - window, now]. 100% mathematically exact, but carries severe O(N) memory overhead per user.

Sliding Window Counter

Approximates traffic by weighting counts from current and previous fixed windows: count = current_window_count + previous_window_count * ((window_size - offset) / window_size). O(1) memory with < 0.05% error rate.

AlgorithmBurst HandlingMemory FootprintComputational CostBoundary Edge Case Vulnerability
Fixed Window CounterDisallows smooth burstsLowest (1 integer per user)O(1) atomic incrementVulnerable to 2x boundary traffic spikes
Token BucketPermits bounded bursts (up to B)Very low (timestamp + token float)O(1) mathematical refillNone (bounds burst to B, average to R)
Leaky BucketSmooths bursts to constant rateLow (bounded FIFO queue)O(1) processingNone (smooth deterministic output)
Sliding Window LogPermits natural burstsHigh (O(N) timestamps per user)O(log N) sorted set opsZero boundary vulnerability (exact precision)
Sliding Window CounterPermits smooth burstsVery low (2 integers per user)O(1) mathematical interpolationMinimal (~0.05% estimation variance)

3. Naive Collapse & Atomic Redis Lua Implementation

A common production vulnerability is implementing rate limiting using separate Redis GET and SET/INCR operations across distributed API gateway nodes.

snippet.texttext
1
2
3
4
5
6
7
8
9
NAIVE CHECK-THEN-ACT RACE CONDITION (Client limit: 10 req/min, current count: 9):
Gateway 1: GET count (=9) --------------------------> INCR count (=10) -> ALLOWED
Gateway 2: ------> GET count (=9) -> INCR count (=11) -------------------> ALLOWED (BURST VIOLATION!)

ATOMIC REDIS LUA EXECUTION (Single-Threaded Engine):
Gateway 1 & 2 -> Redis EVAL (Lua Script)
                 |-- Script runs to completion without interruption
                 |-- Request 1: Tokens decremented from 1 to 0 -> ALLOWED (HTTP 200)
                 +-- Request 2: Tokens = 0 -> REJECTED (HTTP 429)
snippet.lualua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
-- Production-Grade Atomic Token Bucket in Redis Lua
-- KEYS[1]: ratelimit:{user_id}:tokens, KEYS[2]: ratelimit:{user_id}:last_updated
-- ARGV[1]: capacity (B), ARGV[2]: refill_rate_per_sec (R), ARGV[3]: now_seconds, ARGV[4]: requested (1)

local capacity = tonumber(ARGV[1])
local refill_rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])

local last_tokens = tonumber(redis.call('get', KEYS[1]))
local last_updated = tonumber(redis.call('get', KEYS[2]))

if last_tokens == nil then
    last_tokens = capacity
    last_updated = now
else
    local elapsed = math.max(0, now - last_updated)
    last_tokens = math.min(capacity, last_tokens + elapsed * refill_rate)
    last_updated = now
end

if last_tokens >= requested then
    last_tokens = last_tokens - requested
    redis.call('set', KEYS[1], last_tokens)
    redis.call('set', KEYS[2], last_updated)
    local ttl = math.ceil((capacity - last_tokens) / refill_rate) + 60
    redis.call('expire', KEYS[1], ttl)
    redis.call('expire', KEYS[2], ttl)
    return {1, math.floor(last_tokens)} -- 1 = Allowed, remaining tokens
else
    return {0, math.floor(last_tokens)} -- 0 = Denied (HTTP 429)
end

Use it when

Deploy centralized Redis Lua rate limiting at the API Gateway layer to enforce global tenant quotas and prevent abuse across distributed gateway instances.

Reconsider when

Use local in-memory token buckets (per-container) when handling hundreds of thousands of requests per second where Redis network roundtrips (~1ms) exceed latency budgets.

4. Ingress Protocol, Headers & Stripe 4-Tier Defense

  1. 01Ingress Interception: Client sends POST /v1/charges. Gateway extracts IP address and authenticated API key.
  2. 02Atomic Lua Evaluation: Gateway executes Redis EVAL with token bucket parameters. Redis computes remaining capacity in ~0.5ms.
  3. 03Successful Acceptance: If tokens remain, gateway attaches standard IETF headers: X-RateLimit-Limit: 100, X-RateLimit-Remaining: 42, X-RateLimit-Reset: 1698421000. Request forwards to downstream services.
  4. 04Rejection (HTTP 429): If tokens < 1, gateway rejects request immediately with HTTP 429 Too Many Requests and Retry-After: 4. Zero load reaches internal microservices.

Stripe: 4-Tier Rate Limiting Architecture

Stripe operates four distinct defense layers: 1) Request Rate Limiter: Bounded at the edge to block brute-force bursts by IP. 2) Tiered Limiter: Prioritizes revenue-generating live charges over non-critical webhook queries. 3) Concurrent Request Limiter: Restricts simultaneous in-flight requests per tenant to prevent thread starvation. 4) Fleet Usage Load Shedder: Sheds non-critical and test-mode requests when core infrastructure latency exceeds critical thresholds.

Cloudflare: Localized Edge Point of Presence (PoP) Limiting

Cloudflare limits traffic locally within each Point of Presence (PoP) using Twemproxy and Memcached clusters rather than maintaining a single global counter. This trades strict global precision for sub-millisecond evaluation and complete fault isolation across geographical regions.

5. Topology Trade-Offs: Centralized vs. Local vs. Hybrid

ArchitectureAccuracy & PrecisionLatency ImpactFault Tolerance / Failure ModeInfrastructure Overhead
Centralized (Redis Cluster)Strictly Global (100% consistent across all gateways)Adds ~0.5 – 1.5ms network hop to every API requestIf Redis fails: requires fail-open vs. fail-closed strategyModerate (HA Redis cluster)
Local In-Memory (Per Gateway)Approximate (fleet limit = per_node_limit * N_nodes)Sub-microsecond (< 1 µs in-process memory read)High resilience (isolated per node)Zero infrastructure overhead
Hybrid Batch AllocationHigh (~98% precision via batch synchronization)Ultra-low (< 0.1ms; local checks with async sync)Graceful degradation if central store unreachableModerate (sync background thread)

Multi-Region Rate Limiting via Batch Allocation

To avoid 100ms cross-region latency hops on every API call, multi-region architectures partition global quotas into regional allocations (e.g. 60% US-East, 30% EU-West, 10% AP-South). Regional gateways check against local Redis clusters and run background batch reconcilers every 1–2 seconds to reallocate unused quota.

6. Failure Modes & Interview Gauntlet

  1. 01The Fail-Open vs. Fail-Closed Dilemma: What happens when the centralized Redis rate-limiter cluster crashes or becomes unreachable?
  2. 02Fail-Open: Allow all requests through without rate limiting. Preserves availability for legitimate paying users, but risks overwhelming downstream databases during attacks.
  3. 03Fail-Closed: Reject all requests with HTTP 500/429. Protects downstream databases, but converts a cache blip into a total outage for 100% of legitimate users.
  4. 04Production Best Practice: Fail-Open with Emergency Local Fallback: If Redis times out (> 10ms), fail open into a conservative local in-memory token bucket on each gateway node to cap catastrophic bursts.

Deep-Dive Q1: What is the boundary-condition flaw of Fixed Window Counters, and how does Sliding Window Counter resolve it?

Model Answer: In a Fixed Window algorithm, time is divided into static chunks (e.g. 1-minute windows). If the limit is 100 req/min, an attacker can send 100 requests at 00:00:59 and another 100 requests at 00:01:01. Both requests pass because they fall into separate windows, but the server absorbs 200 requests within a rolling 2-second window (a 2x burst!). The Sliding Window Counter resolves this by interpolating the previous window count based on elapsed time: count = current_count + previous_count * ((window_size - offset) / window_size). At 00:01:01, offset = 1s, yielding count = 100 + 100 * (59/60) = 198 reqs, correctly rejecting the traffic.

Deep-Dive Q2: Why should high-scale systems prefer Sliding Window Counter over Sliding Window Log?

Model Answer: Sliding Window Log stores every single request timestamp in a Redis Sorted Set (ZSET). A high-volume tenant issuing 10,000 requests per minute requires storing 10,000 64-bit integers (~80 KB RAM plus Redis data structure overhead). For 1 million active users, this demands over 80 GB of expensive Redis RAM just for rate limiting timestamps, accompanied by heavy ZREMRANGEBYSCORE CPU usage. Sliding Window Counter approximates the exact same behavior using only two integer keys (current and previous window counters), consuming less than 64 bytes per user—a 1,000x reduction in memory overhead with an estimation error under 0.05%.

Active Recall Checkpoint

Why must Redis rate limiting logic be executed via Lua scripting rather than multiple individual Redis commands?

Hint: What happens when two API gateway instances check the remaining token count simultaneously?

Show answer & explanation

Executing separate Redis commands creates a check-then-act race condition: two API gateway instances can read the same remaining token count at the same millisecond, both conclude tokens remain, and both decrement, allowing traffic to exceed configured limits. Redis executes Lua scripts atomically on its single-threaded event loop, guaranteeing that no other command or script can run concurrently during token evaluation.

Active Recall Checkpoint

What standard HTTP status code and response headers should an API gateway return when a client exceeds its rate limit?

Hint: Refer to RFC 6585 and IETF conventions.

Show answer & explanation

The gateway must return HTTP 429 Too Many Requests. It should include the following headers: Retry-After: <seconds> (how long to wait before retrying), X-RateLimit-Limit: <max> (the quota capacity), X-RateLimit-Remaining: 0 (indicating exhaustion), and X-RateLimit-Reset: <timestamp> (Unix epoch time when the quota window resets).

Hardware Bounds & Sizing Math

Topic sizing snapshot

The four numbers that materially affect this architecture. Use them as bounds, not as a generic calculator embedded in every lesson.

Peak QPS

300,000 RPS evaluation

5-Yr Storage

N/A (Transient counters)

Bandwidth

2 Gbps telemetry traffic

Working Set RAM

16 GB Redis cluster for 50M active user counter keys

Production Postmortem & Verification

Production Incident Postmortem

  1. Trigger / Setup: A distributed rate limiter checks Redis key and increments counter in two separate network commands.
  2. Production Symptom: Race condition under concurrency: 50 concurrent requests see count < 100 and all pass through, breaking rate limits.
  3. Root Cause & Fix: Distributed rate limiting checks and increments must be atomic; execute evaluation inside an atomic Redis Lua script.

Acceptance Criteria & Outcomes

  • Evaluate memory footprint and accuracy of Token Bucket, Fixed Window, and Sliding Window Counter.
  • Design an atomic distributed rate limiting architecture capable of handling 500,000 RPS with sub-millisecond overhead.
  • Define graceful degradation fallbacks when the distributed rate limiting cache cluster fails or partitions.

End-of-Chapter Synthesis & Key Takeaway

Rate limiting protects downstreams from resource exhaustion and abuse. Pick Token Bucket for general traffic bursts, Sliding Window Counter for strict SLA enforcement, and use centralized atomic Redis Lua scripts to eliminate race conditions across distributed API gateways.

Architectural & Technical Interview Review

Core FAANG Interview Questions

3 Questions
Q01
How do you choose between the Token Bucket and Sliding Window Counter algorithms?
Click to reveal model answer →Hide answer ↑Requirements & Rate Rules

Choose Token Bucket when your system can comfortably tolerate brief bursts of traffic (up to the bucket capacity) while maintaining a steady long-term rate, which is ideal for REST APIs. Choose Sliding Window Counter when you need strict mathematical quota compliance without burst leakage across window boundaries (e.g. strictly 100 requests per minute with no boundary spikes).

Source section: Requirements & Rate Rules
Q02
How do you implement atomic distributed rate limiting across a cluster of 50 API gateway servers?
Click to reveal model answer →Hide answer ↑Request Flows & Architecture

Use centralized Redis clusters executing Lua scripts. A single Redis Lua script runs atomically and sequentially on the Redis server, performing get, increment, and TTL updates in 1 RTT without race conditions. Return standard HTTP 429 Too Many Requests with headers: X-RateLimit-Limit, X-RateLimit-Remaining, and Retry-After.

Source section: Request Flows & Architecture
Q03
What is the fallback strategy when the centralized Redis rate limiter cluster goes down?
Click to reveal model answer →Hide answer ↑Failure Review & Trade-offs

Decide between Fail-Open and Fail-Closed based on the business domain. For billing, security, or payment gateways, Fail-Closed (reject or throttle to protect from fraud). For public user content, social feeds, and search, Fail-Open (allow traffic through to avoid a total outage) while logging alerts and falling back to in-memory local token buckets per gateway node.

Source section: Failure Review & Trade-offs

Production Pitfalls & Anti-Patterns

Critical Failure Anti-Patterns

!

Read-Modify-Write Concurrency Race Conditions

Fetching a counter from Redis, incrementing in app memory, and saving it back allows concurrent threads to overwrite each other, allowing users to exceed rate limits by 10x.

Production Mitigation: Execute increments via atomic Redis INCR or custom Redis Lua scripts.

!

Fixed Window Boundary Burst Spikes

Fixed window counters allow double the quota if a user sends their maximum requests at the end of window 1 and beginning of window 2.

Production Mitigation: Use Sliding Window Log or Sliding Window Counter to smooth traffic across moving intervals.

!

Rate Limiting by IP in NAT Environments

Limiting by client IP will accidentally throttle an entire university, corporate office, or mobile carrier sharing a single outbound gateway.

Production Mitigation: Rate limit by authenticated user ID or API key, falling back to IP + User-Agent fingerprinting only for unauthenticated endpoints.

Primary Documentation & Specifications

Better Rate Limiting with Redis Sorted Sets (Figma Engineering)
Previous LessonMessage Brokers, Event Streaming & Task QueuesCore InfrastructureNext LessonIdempotency & Safe RetriesDistributed Mechanics

In this chapter

01Architectural Question02Interactive Model031. What is it & Why it Matters042. Algorithmic Foundations: 4 Core Approaches053. Naive Collapse & Atomic Redis Lua Implementation064. Ingress Protocol, Headers & Stripe 4-Tier Defense075. Topology Trade-Offs: Centralized vs. Local vs. Hybrid086. Failure Modes & Interview Gauntlet09Sizing Snapshot10Production Verification11Synthesis & Review12Primary References

Chapter promise

Algorithm trade-offs: Token Bucket vs Leaky Bucket vs Fixed Window vs Sliding Window Counter