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.
Observation, decision, and call sequence
Reported operation durations.
500 ms illustrates the comparison, not a recommended setting. Arrows are calls and replies. Your application calls the database directly; RateLimitly supplies decisions and receives reports.
Read the numbered steps in order. The explanation below follows the narration.
Keep observation, decision, and action distinct.
The tracker observes; the guard decides; your application enforces the decision.
The tracker observes; the guard decides; your application enforces the decision.
A latency tracker is a shared record of reported operation durations. It observes. A guard is the check that uses those observations to decide whether new work may start. A threshold is the configured duration at which that guard refuses work. These are three distinct things: observations, a decision, and the application's action.
Before starting a protected database operation, the client asks for admission. With a positive threshold of five hundred milliseconds, a reported signal at five hundred or above causes the current guard to refuse. Setting this threshold to zero disables that latency guard; it does not mean refuse every request.
If admitted, the application does the database work, measures its duration, and reports afterward. If refused, it does not start that protected work. The client library does not automatically rewrite the application into a cheaper one. Your application handles the decision.
A tracker by itself does not stop anything. A guard checked after the expensive work is too late. Put the decision before the work it is meant to prevent, and make refusal a real branch in the program.
The tracker holds a shared record of measurements supplied by clients. Observing those durations is separate from making an admission decision or stopping application work.
The guard uses the tracker’s observations to decide whether new work may start. The measurement record and the admission check have different responsibilities; they are not interchangeable names for the same thing.
The threshold supplies the guard’s configured comparison boundary. It is distinct from the observed durations: observations describe measured work; the threshold is the policy applied to those observations.
Observations inform the guard, but application code must start or skip the protected operation. Merely recording a latency metric does not prevent work from reaching a database.
Only the first side call exists at this step: application to RateLimitly. The database query has not begun. Put this check before acquiring or consuming the resource the guard is intended to protect.
The comparison includes equality. Below this illustrative positive threshold, the latency guard allows on this signal; at 500 ms or above it refuses. Other required checks can still refuse an operation.
The zero setting is an explicit configuration exception. It disables this latency guard rather than refusing every nonzero-latency operation. It does not disable other required admission checks.
Follow the direct application-to-database call. RateLimitly is a separate admission side call, not a proxy carrying the query. The result and subsequent report have not been shown yet.
The result returns directly from the database to the application. Measure from the agreed start to finish; this is the duration of the protected work, not the duration of the admission side call.
The last arrow reports the measured duration from the application to RateLimitly. That observation can inform future admissions; it does not retroactively prevent or cancel the work just measured.
The refusal comes back to application code, which does not start that database operation. There is no query arrow to the database on this branch. Return a correct cheap fallback or an explicit refusal.
The library does not automatically rewrite an expensive request into a cheap alternative. Your application handles the answer and must not perform the same protected operation while pretending to fall back.
All three responsibilities are needed: an observation, a guard decision, and application code that obeys it. Recording durations while unconditionally running the expensive operation leaves that work unprotected.
Return to the first call in the sequence. A guard evaluated after a database operation cannot prevent that operation. Test the refused path by checking that the protected call was never made.
Content revision: 07c7dd00db17
Download review copy (27 lessons)You report durations but never check before work. Is the database protected by those reports alone?
No. Reports provide observations. An admission check and an enforced refusal branch are needed before new work.
Use the same API key with matching tracker identities and settings across client libraries. Check before the protected operation, branch on the result, and report measured admitted work afterward.
A positive threshold refuses at equality or above; zero disables this guard. A tracker is not an automatic proxy or application rewrite.
Start here. No earlier lesson is required.
Application-owned local admission protects room to finish work and respond.
Use the same API key, tenant bucket name, window, and allowance across every client.
Define one meaningful stopwatch boundary before interpreting its measurements.
Thresholds belong to the guard and need not be part of tracker identity; operations can use different thresholds against the same shared observations.
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.