01Rate limiting02Databases03Caching04Flash sale05Idempotency06Circuit breaker07Queues & DLQ

Lab / 01 · Rate limiting

How do you stop one user from taking down your API?

A rate limiter decides, in under a millisecond, whether a request gets through. Here’s a token bucket running live. Give each user a bucket of tokens; every request spends one; tokens refill at a steady rate. Empty bucket means 429.

LIVE · capacity 5 · refills 1 token / 2s
GET /lab/limited
Your bucket
tokens: 5 / 5
0
200 OK
0
429 Too Many
// Network — same as F12 → Network on the live site
Send a request to see the response headers.

On the live site these requests hit a real Spring Boot endpoint backed by Redis. Open your Network tab and the headers above appear there too.

⚡ k6 Load Test Benchmark Results

k6 v0.50.0 · 500 requests
p95 Latency
1.2 ms
Redis Atomic Lua
0.45 ms
Burst 10 Test
5 admitted · 5 blocked (429)
Steady State
1 req / 2s · 0 drops

The architecture

The check happens before your request ever reaches business logic, and the counter lives in Redis so every server instance shares one view of each user.

Client
your browser
Rate-limit filter
Spring OncePerRequestFilter
Redis
Lua script, atomic
Controller
only if allowed
bucket empty → 429 + Retry-After, controller never runs

The core of it

-- token_bucket.lua (runs atomically inside Redis) local tokens = tonumber(redis.call('HGET', KEYS[1], 'tokens') or ARGV[1]) local last = tonumber(redis.call('HGET', KEYS[1], 'ts') or ARGV[3]) local refill = math.floor((ARGV[3] - last) / ARGV[2]) tokens = math.min(ARGV[1], tokens + refill) if tokens < 1 then return {0, tokens} end redis.call('HSET', KEYS[1], 'tokens', tokens - 1, 'ts', ARGV[3]) return {1, tokens - 1}
Full source on GitHub →

Trade-offs I weighed

Token bucket

Allows short bursts, smooth over time. Good default for user-facing APIs.

Sliding window

Stricter and fairer at window edges, but costs more memory per user.

Why Lua in Redis

Read-check-write in one atomic step, so two servers can’t both spend the last token.

← All demosNext: 02 Databases →