Rate Limiter — Masterclass
Three additional artifacts a Staff/Principal candidate should be able to produce for this problem: an Architecture Decision Record, a business-driven design exercise, and a production incident scenario.
1. Architecture Decision Record
The format working architects use to document a decision so future teams understand context, options, and reversal conditions.
Redis Cluster (sharded) + hierarchical Lua atomic script (via EVALSHA) + hashtag routing for co-located hierarchies + fail-open with degraded limit fallback — over EVAL (uncached), naive-per-instance token bucket, CRDT-only counters, sticky-routing per user, or third-party (Cloudflare Rate Limiting).
- 1M RPS baseline, 5M+ RPS peak (during customer bursts)
- 100K+ unique API keys across ~500 tenants
- Hierarchical limits: per-user + per-endpoint + global (3 checks per request)
- p99 check latency: <5ms (every API request pays this cost)
- Fail-open policy: over-serving > rejecting legitimate users on our infra failure
- Accuracy: 5% over-serve during merge windows is acceptable; blatant over-serve (10x limit) is not
- Support burst allowance: users can spike 5x limit for 10 seconds
- Multi-tenant isolation: one tenant hitting limits should not affect other tenants' latency
Redis Cluster + hierarchical Lua atomic script (EVALSHA cached) (chosen)
- Single Lua script atomically checks per-user + per-endpoint + global in ONE round-trip
- EVALSHA is 10-20% faster than EVAL (script cached at Redis)
- Same-AZ TCP + Redis event loop = sub-2ms p99
- Hashtag routing co-locates per-user + per-endpoint keys on same shard (no cross-shard)
- Redis Cluster scales horizontally to 16M+ ops/sec
- Fail-open with degraded fallback (10 RPS instead of 100) prevents attacker exploitation
- Stripe + Cloudflare + Google actual pattern
- Redis SPOF per shard — plan for shard failover
- Lua script authoring is harder to test than SDK code
- Hashtag routing convention required across all clients
- Cross-shard queries (analytics: 'top rate-limited tenants') require Redis SCAN + client aggregation
Redis + EVAL (uncached Lua)
- Simpler client code (no script hash management)
- Same atomic semantics as EVALSHA
- Redis parses Lua on every EVAL call — 10-20% slower
- At 1M RPS baseline, that 10-20% = $10-20K/mo extra Redis capacity
- Same architecture otherwise — just slower
Naive per-instance token bucket (Guava RateLimiter / golang.org/x/time/rate)
- Sub-microsecond check latency (in-process, no network)
- Zero external infrastructure
- Team already knows the libraries
- Over-serves proportional to instance count (30 instances = 30x limit)
- Cannot enforce global rate limits
- Bucket state lost on restart (fresh window every deploy)
- Cannot support hierarchical limits without shared state
CRDT-only counters (G-Counter with async merge)
- No SPOF — fully distributed
- Fault-tolerant to any single-node failure
- Regional locality — no cross-region synchronous coordination
- 5-10% over-serve during merge windows (10-60 seconds)
- Complex reasoning about eventual consistency at check time
- Only makes sense at multi-region scale (L7+); overengineering at L6
- Doesn't help with sub-5ms latency budget (still need Redis for check)
Sticky routing (same user → same instance)
- Preserves in-process bucket accuracy
- No Redis dependency
- Load balancer complexity (hash-by-user routing)
- Instance hotspot: popular users overload specific instances
- Fails on instance restart (user's state lost mid-window)
- Doesn't work for hierarchical limits (per-endpoint routing conflicts with per-user)
Third-party (Cloudflare Rate Limiting)
- Zero infrastructure to run
- 300+ edge PoPs — global sub-30ms
- DDoS protection built-in
- Cloudflare team handles all ops
- $0.05 per million requests × 1M RPS × 86400s × 30d = $130K/mo at sustained baseline
- Vendor lock-in on foundational infrastructure
- Custom logic (per-endpoint burst allowances, tenant reputation) hard to express
- Latency depends on Cloudflare's edge — typically <30ms globally but not <5ms
Redis Cluster + hierarchical Lua atomic script + EVALSHA + hashtag routing + fail-open with degraded limit fallback
- Cloudflare Rate Limiting — $130K+/mo at our scale (30x self-hosted); vendor lock-in on foundational infra
- Naive per-instance token bucket — architecturally broken above ~50K RPS (n-instance over-serve)
- CRDT-only counters — overengineering at single-region L6; 5-10% over-serve during merge window
- Sticky routing — brittle (instance hotspots, restart data loss); doesn't work for hierarchical
- EVAL (uncached Lua) — 10-20% slower = $10-20K/mo extra Redis capacity vs EVALSHA
- Accept Redis SPOF per shard — plan for shard failover (30-60s of degraded-limit fallback)
- Accept Lua script authoring discipline (harder to test than SDK code)
- Accept hashtag routing convention across all clients (documented, enforced by client library)
- Accept cross-shard analytics queries require SCAN + client aggregation (rare, batched)
- Accept 3ms Redis timeout as aggressive but non-negotiable (falls back to degraded limit on timeout)
- Accept fail-open policy over fail-closed — attacker exploitation mitigated by degraded limit
- Accept dedicated Rate Limiting Platform team (5-10 engineers) — foundational infra warrants first-class ownership
- Every API request pays 1-3ms rate-limit latency — this is the tax on the entire product
- Redis Cluster becomes critical-path infrastructure — 24/7 SRE + monitoring
- Hashtag routing convention flows through every client library + service integration
- Fail-open policy documented and understood by security + compliance teams
- Redis shard failover triggers degraded-limit fallback for ~30-60s — customer-visible during incidents
- Attackers know we fail-open — deliberate DDoS + Redis-DOS is a real threat vector (mitigated by degraded limit)
- Rate-limiting logic becomes reusable primitive for feature flags, tenant quotas, spam control
- If Redis Cluster ops cost exceeds 3 engineers dedicated → evaluate Cloudflare enterprise (accept 30x cost)
- If we expand to <100K RPS steady after cost cutting → naive per-instance may be acceptable again (regressive but simpler)
- If multi-region expansion + sub-30ms cross-region rate limits required → CRDT counters unavoidable
- If we get acquired by Cloudflare → their infrastructure becomes free
2. Business constraint exercise
Given real-world constraints (team size, budget, deadline), what architecture do you propose — and how do you push back when leadership asks for the wrong thing? This teaches engineering judgment.
You are the platform engineering lead at a $50M ARR B2B SaaS company (200 engineers). CTO drops in Monday: 'Cloudflare Rate Limiting proposal: $200K/year, 300+ edge PoPs, DDoS included, zero infrastructure. Alternative: build our own on Redis. Which is right? Board decision Wednesday.'
- 1$50M ARR B2B SaaS (150 enterprise customers)
- 2API is 40% of revenue — rate limiting failure = major incident
- 3500K RPS baseline, 5M RPS during customer batch runs
- 45-engineer Platform team (of 200 total engineers)
- 5Existing infrastructure: AWS us-east-1 primary, EU-west failover
- 63-year budget horizon (board wants forecast)
- 7Existing customer: 3 largest customers do batch runs at 3 AM (each 500K RPS burst for 2 hours)
- 8Cloudflare proposal: $200K/yr = $0.05 per million requests + $50K/yr enterprise support
- 9DDoS protection: currently paying $30K/yr AWS Shield Advanced (marginal but existing)
What do you recommend to the board Wednesday? Show 3-year cost projection + operational cost + strategic trade-offs. Anticipate the board's follow-up: 'why not both?' and address it.
3. Production incident scenario
You are on-call at 3:47am. p99 has spiked. Walk through the investigation, hypothesis, mitigation, and postmortem. This teaches real production reasoning — not just design.
PagerDuty alert at 12:47pm ET on Black Friday. Redis Cluster shard-7 (of 16) failed hard (kernel panic). Failover to replica in progress. Meanwhile: Rate Limiter fail-open policy triggered — 6.25% of API traffic bypassing limits for shard-7 keys. Marketing dashboards show anomalous traffic spike from suspicious IPs. Attackers may be exploiting the fail-open window.
- redis.shard7.status: DOWN at 12:47:03 (kernel panic on primary)
- redis.shard7.failover_status: in_progress (target: 30-60s)
- rate_limiter.failure_count.per_second: 62K (6.25 pct of 1M RPS shard-7 traffic)
- rate_limiter.fail_open_allowed_percent: 6.25 pct (shard-7 keys only)
- api.total_requests_per_second: 1.15M (was 1M baseline — 15 pct spike above expected)
- api.top_endpoint_requests.suspicious: /v1/coupons/apply spike 40x baseline (12K/sec, was 300/sec)
- api.tenant_id.7f3e2a: 480K RPS (was 2K RPS baseline — 240x spike from single tenant)
- iam.suspicious_ip_range: 47.201.0.0/16 hitting 45K RPS from 12:47 onwards (was 200 RPS baseline)
- downstream.checkout_service.cpu_percent: 92 (was 40 baseline — attacker traffic reaching origin)
- downstream.database.connection_pool_wait_p99: 800ms (was 20ms baseline)
- 12:47:03 redis-shard7-primary: 'kernel panic - not syncing: Fatal exception'
- 12:47:04 rate-limiter-service: 'shard-7 timeout after 3ms — triggering fail-open policy'
- 12:47:05 rate-limiter-service: 'ALERT: fail-open triggered for shard-7 keys (6.25 pct of traffic)'
- 12:47:12 iam-service: 'anomalous 100x traffic spike from IP range 47.201.0.0/16'
- 12:47:15 checkout-service: 'connection pool at 95 pct — queueing requests'
- 12:47:23 rate-limiter-service: 'shard-7 failover to replica in progress (ETA 30s)'
- 12:47:45 marketing-dashboard: 'anomalous coupon-apply traffic 40x baseline'
- 12:47:58 redis-shard7-replica: 'promoted to primary'
- 12:48:03 rate-limiter-service: 'shard-7 recovered — fail-open policy disengaged'
- Redis shard-7: DOWN → RECOVERING (failover in progress, 30-60s)
- Redis shards 0-6, 8-15: HEALTHY — 93.75 pct of traffic unaffected
- Rate limiter service: DEGRADED — fail-open for 6.25 pct of traffic
- API gateway: HEALTHY at LB layer but seeing 1.15M RPS (vs 1M baseline)
- Checkout service: DEGRADED — 92 pct CPU (was 40 pct), connection pool 95 pct
- Payment service: HEALTHY but seeing suspicious /v1/coupons/apply traffic
- Postgres: DEGRADED — connection pool wait p99 800ms
- Fraud detection: HEALTHY — flagging 47.201.0.0/16 as suspicious
It's 12:47:15pm. Fail-open policy is triggered for shard-7 keys — 6.25% of traffic. Marketing dashboards show a 40x spike on /v1/coupons/apply. What's your SINGLE highest-priority action in the next 60 seconds?
Assuming degraded fallback is now active. IP range 47.201.0.0/16 is doing 45K RPS from previously-idle IPs. What's your next action?
Redis shard-7 recovered at 12:48:03. Fail-open policy disengaged. But between 12:47:03 and 12:48:03, some attackers got through the fail-open + degraded-limit window. How do you audit the damage?
Postmortem the next day. Name 3 action items ranked by impact, with quantified reduction in incident probability or blast radius for each.
Draft the customer-facing status page update at T+10 minutes (12:57pm — after Redis recovery + attack mitigation). Constraint: honest, no jargon, no over-promising, no 'never again' language.
Learn these first
- Redis atomic operations + Lua scripting (EVALSHA vs EVAL)
- Token bucket vs sliding window vs fixed window algorithms
- Hierarchical rate limits (per-user AND per-endpoint AND global)
- Fail-open vs fail-closed on infrastructure failure
Where this appears in the curriculum
This is the Gold Standard.
Every other system will eventually have a masterclass tab like this one. The pattern proven here — ADR + business exercise + incident scenario — scales to all 50+ problems on the platform.