Failure-path capacity · July 18, 2026
Chaos testing 429s, retries, and tenant isolation before they become lost revenue
A dependency can slow down, return 429, reset a connection, duplicate a callback, or recover unevenly. This guide turns those faults into controlled tests for retry budgets, idempotency, queues, circuit breakers, per-tenant bulkheads, and revenue exposure.
Method
What this comparison measures
- Inject one fault at a time under a steady workload, then add recovery and a bounded combination test.
- Measure HTTP status alongside attempted calls, useful completions, retries, queue age, tenant impact, and recovery time.
- Use the Google SRE retry-amplification example and IETF status semantics as documented evidence; revenue values are explicit models.
- Run faults only inside an isolated contour with deterministic provider doubles and a verified teardown path.
429 is a control signal, not a generic error
RFC 6585 defines 429 Too Many Requests for rate limiting and allows a Retry-After header describing how long to wait. A client that immediately retries converts the provider's protection signal into more load.[1]
A safe policy classifies failures. A validation error is permanent. A 429 may be retriable after the provider's delay. A timeout has an unknown side-effect state. A 503 may indicate overload. A partially streamed response may be impossible to replay invisibly.
Timeouts should fit inside the user-operation budget. Retries should be bounded, use exponential backoff with jitter, and happen at one chosen layer. AWS documents these patterns because synchronized retries can magnify a small failure into a full outage.[3][4]
| Fault | Default action | Evidence | Unsafe reaction |
|---|---|---|---|
| 429 + Retry-After | Wait, jitter, bounded retry | Delay, attempts, final result | Immediate parallel retry |
| Timeout | Cancel within operation budget; query state if side effects possible | Deadline and remote outcome | Assume nothing happened |
| 500 / 503 | Retry only idempotent work with budget | Provider health and attempts | Retry at every layer |
| Connection reset | Classify by operation phase | Bytes sent/received and idempotency key | Blind duplicate POST |
| Malformed success | Fail closed and retain response class | Schema error | Treat HTTP 200 as success |
| Slow recovery | Probe gradually; keep admission control | Useful throughput and saturation | Release entire queued backlog |
Retries multiply across layers
Google SRE gives a concrete warning: if database, backend, frontend, and JavaScript layers each make four total attempts, one user action can create 64 database attempts. Retrying at several layers multiplies attempts instead of adding them.[2]
The same chapter models a backend capable of 10,000 QPS receiving 10,100 QPS. One hundred rejected requests retry, raising the next second to 10,200; the retry load grows while useful first-attempt work shrinks. A retry budget contains that amplification.
The chart reproduces the documented multiplicative structure, not an observed CapacityLab customer incident.
One noisy tenant should not consume every worker
Shared connection pools, worker queues, model-provider quotas, and retry budgets create cross-tenant coupling. A single client that overloads one method can occupy concurrency needed by unrelated customers even when their own traffic is normal.
Bulkheads partition scarce resources. Per-tenant concurrency, queue capacity, token budgets, and fair scheduling keep one overload local. Global admission control still protects the service when total safe capacity is reached.
Idempotency prevents a retry from becoming a duplicate purchase or job. Stripe stores the first result for an idempotency key, while AWS describes client request identifiers as a foundation for safe retryable APIs.[6][5]
- Per-operation concurrency caps before the shared dependency pool.
- Per-tenant queue and retry budgets with fair scheduling.
- Idempotency keys persisted through timeout and worker restart.
- Circuit breaker state that does not synchronize every caller into one probe storm.
- Dead-letter and expiry behavior for work that can no longer meet the user promise.
Translate the fault into revenue exposure
A technical error budget becomes actionable when it is connected to product operations. If 120 contribution-producing operations arrive each minute at an $18 contribution margin, a complete fifteen-minute outage exposes $32,400. This is not booked loss: retries, delayed conversion, refunds, and customer recovery change the result.
The point of the model is prioritization. A 429 on image transformation may degrade presentation; a 429 on payment confirmation can hold inventory and revenue. Fault tests should follow business criticality, not infrastructure fashion.
Grafana k6 documents xk6-disruptor for fault injection, and Google SRE explicitly recommends testing overload behavior and graceful degradation. Run the test with steady traffic before the fault, during the fault, and through recovery.[7][2]
Source register
Specifications and prices change. The links make this snapshot auditable.
Sources and commercial facts were checked on 2026-07-18. Prices exclude tax unless the source says otherwise.
- RFC 6585: 429 Too Many Requests ↗RFC Editor · product
- Addressing cascading failures ↗Google SRE · benchmark
- Timeouts, retries, and backoff with jitter ↗Amazon Web Services · product
- Retry behavior in AWS SDKs ↗Amazon Web Services · product
- Making retries safe with idempotent APIs ↗Amazon Web Services · product
- Idempotent requests ↗Stripe · product
- Inject faults with xk6-disruptor ↗Grafana Labs · product
- k6 thresholds ↗Grafana Labs · product