Why Our Rate Limiter Fails Open
Every request to a public mock passes through a rate limiter before it reaches your routes. It's backed by Redis. And when Redis is unreachable, MockBase doesn't block the request — it lets it through. That's not an oversight; it's the deliberate design.
How the limiter works
It's a fixed-window counter, not a sliding-window or token-bucket scheme. For each caller, the current window is computed from wall-clock time (now - (now % window)), and the Redis key embeds that window boundary directly: ratelimit:{namespace}:{identifier}:{window_start}. There's no cleanup job — a key that isn't touched again simply expires on its own.
A single check does two things in one round trip:
INCR ratelimit:engine:<identifier>:<window_start>
EXPIRE ratelimit:engine:<identifier>:<window_start> <window_seconds>
Both commands are pipelined (redis.pipeline(transaction=True)), so a rate-limit check costs one network round trip, not two, on every single request to the mock engine. Namespaces keep buckets independent — the public mock engine (engine, 120 requests per 60-second window by default) and the authenticated management API (60 requests per 60-second window) never share a counter, so heavy traffic on one can't burn through the other's allowance.
The failure mode that actually matters
Here's the part that's easy to get backwards: what happens when the INCR/EXPIRE pipeline throws — Redis is down, a network blip, a timeout?
The limiter catches the exception and returns "allowed," full remaining quota, no retry-after. In other words: on any Redis error, every request passes.
That's the opposite of what a lot of rate limiters do by default, and it's a real tradeoff, not a free lunch. A "fail closed" limiter — reject everything when the backend is unreachable — protects you from abuse during an outage but takes down every mock the moment Redis has a bad five minutes. For an internal admin API, fail-closed is often the right call. For a public mock engine that other people's CI pipelines and demos depend on, an outage in the rate limiter taking down the entire product is a worse failure than temporarily having no rate limiting at all.
So the priority order here is explicit: availability of the mock engine beats strict enforcement of the limit. Rate limiting is a defense against accidental floods and casual abuse, not the last line of defense against a determined attacker — and it's not worth sacrificing uptime for every legitimate caller to enforce it perfectly during a Redis blip.
The tradeoff, stated plainly
This means a sustained Redis outage is technically a window where rate limits don't apply at all. MockBase accepts that risk deliberately: a rare, temporary loss of one abuse-prevention layer is a better failure mode than an infrastructure hiccup in a side dependency taking the whole mock engine offline for every user.
Xerat02
Building MockBase.
More from the blog
Your Mock's Access Token Is Shown to You Exactly Once
Turn on token protection and MockBase generates a token, shows it to you, and then — by design — never shows it to you again. Not in the UI, not through the API. If you didn't copy it, your only move is to rotate: generate a new one and invalidate the old.
Custom Subdomains Match Exactly One Label, on Purpose
Claim a slug and your mock also answers at `<slug>.mockbase.org` instead of only `mockbase.org/mock/<id>`. The routing behind that is a single Host-header check, and it's stricter than it might look — deliberately.