Operating Distributed Quota
This page covers Redis deployment, rollout, degraded modes, and incident response for Impulse distributed quota. It intentionally does not duplicate the schema. Use:
- Resilience, Rate Limits, and Quota for fields, defaults, constraints, and examples
- Quota Policy Contract for stable selector, counter, response, and observability semantics
- Metrics Reference for exact metric names and labels
When Redis Is Appropriate
Use the Redis backend when a quota must be shared across multiple Impulse
instances. Keep scoped_rate_limits or the in-memory quota backend when the
counter is intentionally process-local and a Redis dependency would add no
value.
Distributed quota is contract enforcement, not overload protection. Keep local adaptive admission, inflight caps, circuit breakers, queues, and brownout configured independently.
Redis Deployment Posture
- Place Redis close to the Impulse fleet; quota evaluation is on the request admission path.
- Use a dedicated deployment or logical database when quota load or failure isolation matters.
- Use a distinct key prefix for each environment and contract namespace.
- Restrict Redis network access and credentials to the Impulse deployment.
- Monitor memory, evictions, command latency, connection errors, and saturation of Impulse's Redis inflight cap.
- Synchronize clocks across Impulse instances. The proxy computes fixed-window boundaries from its local wall clock before the atomic Redis script runs.
- Treat Redis replication and multi-region lag as an explicit consistency tradeoff. Atomicity is per request, policy, and composite key on the Redis node executing the script.
Impulse stores a separate key per policy, selector, and window bucket. Keys expire at the window boundary plus a small grace interval. A request with both burst and sustained windows evaluates and updates them in one script call.
Timeouts and Concurrency
Choose the connection timeout for the network path and the command timeout for the latency your request admission path can tolerate. Tight timeouts detect an incident quickly but can move healthy slow responses into fail-open, fail-closed, or local fallback behavior. Loose timeouts consume request latency and inflight capacity.
Set max_inflight to bound Redis work from each Impulse process. Size the Redis
deployment for the aggregate cap across the fleet, not the value from one
instance. Watch for backend_timeout and backend_unavailable while tuning;
raising the cap during Redis saturation usually amplifies the incident.
Failure Posture
Choose the backend-failure policy as a product decision before rollout.
Fail Open
Use fail_open when temporary over-admission is preferable to application
unavailability. A backend timeout, unavailable backend, or evaluation error
admits the request and records a failed-open quota outcome. Local overload
controls continue to apply.
Fail Closed
Use fail_closed when contract strictness is more important than availability.
A backend failure returns 503 Service Unavailable, with a quota backend reason
rather than an overload reason.
Bounded Local Fallback
Local fallback is useful when an outage should retain approximate enforcement without blocking all traffic. It is deliberately bounded and process-local:
- only Redis timeout and unavailable failures enter fallback
- protocol, script, and other backend errors bypass it
- each Impulse instance maintains independent fallback counters
- a successful fallback decision can still deny an exhausted policy
- fallback-capacity exhaustion returns to the configured fail-open or fail-closed behavior
- metrics and runtime state remain degraded while fallback is in use
Do not enable fallback unless per-instance divergence during an outage is
acceptable. Size max_entries for expected active selector/window buckets and
alert before it becomes the next failure boundary.
Rollout Procedure
- Define selectors and windows in a non-production candidate and confirm that route allowlists contain upstream pool names.
- Activate the policy in
shadowmode. - Confirm selector extraction does not produce unexpected missing/invalid outcomes or unbounded policy cardinality.
- Confirm
GET /admin/runtimereports the intended backend and policy set. - Observe Redis latency, quota backend health, and would-deny decisions under representative traffic.
- Exercise Redis timeout and unavailable scenarios and verify the chosen fail-open, fail-closed, and fallback behavior.
- Switch to
enforcethroughvalidate → preview → activate. - Watch quota outcomes and application error budgets before expanding the rollout.
Use a new key prefix when a rollout must start with empty counters. Reusing a prefix intentionally preserves the active fixed-window namespace.
Migrating Scoped Rate Limits
Scoped rate limits and distributed quota have different missing-key and counter semantics, so migrate by observed behavior rather than translating fields blindly.
- Recreate the rule as a quota policy in
shadowmode. - Make implicit scoped defaults explicit. Typical mappings are route to
selector.route, token tobearer_token, and client toclient_iporpeer_ip. - Account for the difference that scoped rules use a legacy authority/path/ method fallback when their identity is missing; distributed quota reports an explicit missing selector instead.
- Compare shadow outcomes with scoped-rate-limit metrics.
- Enable quota enforcement.
- Remove the scoped rule only after the enforced outcomes match the intended contract.
Incident Triage
Rising 429 Responses
- Check
impulse_quota_policy_outcomes_totalby policy and reason. - Distinguish burst/sustained exhaustion from missing or invalid selectors.
- Confirm the policy and selector dimensions in
GET /admin/runtime. - Determine whether traffic growth, abuse, an identity-source change, or an incorrectly sized contract caused the increase.
Do not widen adaptive admission or inflight limits to address quota exhaustion.
Quota-Related 503 Responses
- Confirm
decision="failed_closed"and inspect the backend reason. - Check
impulse_quota_backend_health_totaland the runtime quota backend health summary. - Correlate Redis command latency, connection errors, availability, memory, and evictions.
- Check whether
max_inflightis saturated. - Restore Redis or intentionally activate a reviewed fail-open/fallback candidate. Do not silently change failure posture during diagnosis.
Not every 503 is quota. Brownout, inflight shedding, backend timeout, and other overload or transport paths also return 503; use the reason and subsystem metrics before acting.
Fallback Is Active
Fallback keeps traffic moving but is an incident state:
- Confirm a fallback-flavored
backend_modeand degraded runtime state. - Restore primary Redis connectivity before local stores approach capacity.
- Expect counters to diverge across instances during the outage.
- After recovery, confirm decisions return to
backend_mode="redis"and backend health observations become successful.
Operator Signals
Primary signals:
impulse_quota_policy_outcomes_total{policy,decision,reason,selector_dimensions,backend_mode}impulse_quota_backend_health_total{backend_mode,reason}GET /admin/runtimequota policy and backend-health summaries
Alert immediately on failed-open, failed-closed, or fallback backend modes. Quota exhaustion may be expected contract behavior; alert on it relative to the policy's expected traffic rather than treating every 429 as an infrastructure failure.