Three interchangeable rate-limiting algorithms -- token bucket, sliding window, and leaky bucket -- each with an in-memory and a Redis-backed implementation behind one Go interface, plus a runnable HTTP service -- fifteen ways the concurrency, correctness, and operability details of a production rate limiter quietly go wrong.
Nobody has started this yet — be first.
Token bucket denies a request that exactly matches remaining tokens
Token bucket backs the pro-tier plan (and any small-capacity policy, e.g. "1 password reset email per key at a time"). Losing the last unit of every configured burst means paying customers systematically get less throughput than the plan they're sold -- worse for small capacities, where "capacity 1" effectively means "capacity 0, always denied." That shows up as support tickets ("your API says I'm rate-limited but I've made zero requests"), erodes trust in the pricing tiers, and is the kind of quietly-wrong behavior that's easy to ship unnoticed because most manual testing checks "does it deny eventually," not "does it admit exactly N."
About this project
A production-shaped rate limiter, as a Go library (pkg/ratelimit) and a runnable HTTP service, implementing three interchangeable algorithms behind one Limiter interface -- Allow/AllowN -- each with both an in-memory and a Redis-backed implementation: token bucket (bursty, refills at a steady rate), sliding window counter (a two-window blend approximating a true sliding window, O(1) memory per key), and leaky bucket (rejects outright once full, drains at a constant rate -- built for smoothing/shaping traffic, e.g. a login brute-force guard). The three in-memory implementations deliberately use three different concurrency primitives -- a per-key mutex for the compound float state in token bucket and leaky bucket, and an atomic compare-and-swap fast path for the sliding window's hot counter -- while every Redis-backed implementation pushes its whole read-decay-check-write sequence into a single Lua script, so Redis's single-threaded command loop gives exact correctness across any number of replicas without a distributed lock.
Leaky bucket fills instead of draining
This algorithm guards login-brute-force-guard -- i.e. it sits directly on the login page, the single most trafficked, most first-impression-critical path in the product. A bug that turns a temporary slow-down into a permanent lockout means any legitimate user who ever tried logging in 5 times in a row (a mistyped password, a forgotten password, a shared office IP with several people logging in) is locked out forever, with no recovery except an operator restarting the whole service -- which resets everyone's state, not just theirs. This reads to customers as "the login page is broken," generates a support ticket per affected user, and can plausibly look like a full outage even though every other system is healthy. It is a self-inflicted denial-of-service against your own users, on the path they hit first.
Eight tickets are bugs deliberately injected into an otherwise-working codebase (an off-by-one that denies a request exactly matching remaining capacity, a leaky bucket whose fill level rises instead of drains -- permanently locking out any user who ever tripped it, a spoofable X-Forwarded-For hop that turns IP-based rate limiting into a bypassable no-op, a Redis Lua script whose boolean-to-RESP-nil conversion panics on every single denied request); seven are enhancements that don't exist yet, several with a compile-only stub so the codebase and the verification tests build before the real implementation is written. Every ticket has a dedicated Go test that goes green when the fix is correct.
STORE_BACKEND=memorygo run ./cmd/serverWork the tickets in tasks/ (ordered easy → hard); each names one Go test in test/:
make test-race # go test -race -count=1 ./...
go test ./test/ -run TestTask01 -v # a single ticket
./test/run_all.sh # all 15, pass/fail summary
How to run it locally, step by step.