Why you shouldn’t use Redis as a rate limiter: Part 1 of 2
A tour of the common Redis-based rate limiter implementations — and the correctness and performance traps each one hides.
We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Rate limiting and admission checks that help reduce overload and protect expensive work.
Set admission budgets for launch spikes and promos before excess work reaches your handlers.
Fewer moving parts to run and monitor. Spend less on Redis and ops.
Add via SDK. Keep your stack. Clear limits your customers understand.
UNDERSTAND OVERLOAD · KEEP USEFUL WORK MOVING
For developers, architects, and managers: 27 narrated lessons on why services overload and how to protect them. No cloud experience required. Start with one request, or jump to any lesson.
Four chapters, one learning path. Read or listen in any order. ~41 minutes total · Times shown at 1×.
Keep enough capacity to finish work and recover.
Contain one customer's excess without punishing everyone.
Reduce work entering a shared dependency when it slows.
Choose, combine, and test the right protections.
Read the explanation or use the course map above. Audio is optional.
Failure cascade
Each copy can create demand on the same resource.
The copies depend on the same ability to complete queries.
Arrows show causal paths. Branches distinguish possible outcomes.
Read the numbered steps in order. The explanation below follows the narration.
Many applications depend on the same database.
Database unavailable → calls wait or fail → unbounded retained app work can cause further failure.
Database unavailable → calls wait or fail → unbounded retained app work can cause further failure.
A dependency is another component an application needs to complete some work. A database used by many application copies is a shared resource: all of those copies depend on its finite capacity. A database query is an operation asking it to read or change data.
Many applications send queries at once. If the database cannot finish them fast enough, active queries, connections, and waiting work can consume resources. Under some configurations, memory exhaustion may stop database operations or make the database unavailable. This is a possible severe outcome, not what every database must do.
The applications now wait for answers or receive connection errors. If they keep admitting requests and retaining waiting work, their own memory can grow. Unprotected application processes can fail as a consequence of the database failure.
With local memory protection, the applications may stay alive by refusing excess work. But can they produce a report that requires the unavailable database? No. Keeping the application process alive is useful, but it does not restore the missing dependency. Unavailability alone also does not prove stored data was lost.
Adding application copies gives this shared database more callers. It does not give the database more capacity.
Active queries, connection state and waiting work can all consume resources. Where the backlog lives depends on pools, limits and the database configuration.
This severe branch is conditional, not inevitable. Allocation failures or terminated database processes may disrupt service; unavailability does not prove data loss.
A missing answer can leave callers waiting. A broken connection can instead report an error quickly. Those are different paths with different retained-work costs.
The cascade requires retained work to keep growing. Fast errors with bounded cleanup do not automatically exhaust application memory.
If application processes exhaust their own memory, the outage has spread beyond the database. More restarting app copies alone will not repair that shared dependency.
Your application can refuse excess work before its own memory is exhausted. This protects its ability to respond, not the availability of the database.
No: a report requiring the unavailable database still cannot be produced. Explicit refusal is different from success, and unavailability does not prove data loss.
Content revision: 07c7dd00db17
Download review copy (27 lessons)Local memory guards keep the apps alive. Are database-backed reports available?
Not while the required database remains unavailable. Process survival and customer functionality differ.
Bound application waiting and database concurrency; treat fast connection errors differently from deadline expiry.
Some databases refuse work or fail only an operation. This scene deliberately illustrates a stated severe database-unavailable branch.
Start here. No earlier lesson is required.
Accepting a request is not the same as delivering a useful result.
100 arrivals/s − 80 completions/s = 20 extra unfinished requests/s.
Timeout, cancellation, connection failure, and operation outcome are different facts.
Waiting work can retain memory. A failed allocation or terminated process can stop service.
240 requests/s ÷ 3 = 80 each; after one loss, 240 ÷ 2 = 120 each.
Application-owned local admission protects room to finish work and respond.
Failure scope depends on database/runtime configuration, connection behavior and memory enforcement. App OOM requires retained work to keep growing; fast errors with bounded admission can avoid it.
Original explanation inspired by Fred Hébert and operational references; no endorsement implied.
AI-generated narration: ElevenLabs Eleven v3, Daniel stock voice. Audio streams only when you start listening.
A tour of the common Redis-based rate limiter implementations — and the correctness and performance traps each one hides.
The myth of infinite serverless scale — why adding machines doesn’t fix overload, and what to do instead.