KyoMel / Yogyakarta, Indonesia

Term 293293 / 344

The Retry Budget Pattern

Month
August 2026
Day
6
Source
2026/august-term.md

Definition:

The Retry Budget Pattern is a resilience strategy that caps the total amount of retry effort a system is willing to spend on a given operation — before it gives up and fails fast. Instead of “retry as many times as it takes” (which can cause retry storms, cascading failures, and resource exhaustion), the pattern pre-defines a budget: a fixed number of retries, a maximum total wait time, or a maximum number of concurrent retries. When the budget is spent, the system stops retrying immediately.

It’s the direct answer to the retry storm problem (which we covered earlier in “Congestion Bug”): naive retries create a positive feedback loop where more failures trigger more retries, which cause more load, which cause more failures. The retry budget breaks that loop by making the system give up gracefully when retries would do more harm than good.

NAIVE RETRIES vs. RETRY BUDGET:
═══════════════════════════════════════════════════════════════

  NAIVE RETRIES (Unbounded — the anti-pattern):
  ──────────────────────────────────────────────────────────

  Request fails ──► retry ──► fails ──► retry ──► fails ──► retry...
                    (forever, or a huge fixed number)

  ┌─────────────────────────────────────────────────────────┐
  │                                                         │
  │  Problem 1: RETRY STORM                                  │
  │  Every failure spawns retries. 10k failing requests    │
  │  → 10k × 5 retries = 50k attempts on an already-       │
  │    struggling service. Makes it worse.                  │
  │                                                         │
  │  Problem 2: RESOURCE EXHAUSTION                         │
  │  Each retry holds a thread/connection/slot. 50k         │
  │  retries hold 50k slots → memory, threads, sockets      │
  │  exhausted.                                             │
  │                                                         │
  │  Problem 3: NO GUARANTEED TERMINATION                   │
  │  The system keeps trying even when success is           │
  │  impossible (service down for 30 min, but code          │
  │  retries for 30 min).                                   │
  │                                                         │
  └─────────────────────────────────────────────────────────┘

  RETRY BUDGET (Bounded — the pattern):
  ──────────────────────────────────────────────────────────

  Budget: max 3 retries, OR max 10 seconds total,
          whichever comes first.

  ┌─────────────────────────────────────────────────────────┐
  │                                                         │
  │  Request fails ──► attempt 1 (fails)                    │
  │  attempt 2 (fails)                                      │
  │  attempt 3 (fails)  ← budget reached                    │
  │  attempt 4 (fails)  ← budget reached                    │
    │  STOP. Give up. Fail fast.                              │
    │                                                         │
    │  ✅ Bound on retry count (no infinite loop)             │
    │  ✅ Bound on time (no 30-min retry for a 10-sec op)     │
    │  ✅ Fail fast — surface the error immediately           │
    │  ✅ Load on downstream is controlled                    │
    │                                                         │
    └─────────────────────────────────────────────────────────┘

Example:

A visual comparison of how the same downstream outage is handled without and with a retry budget — showing the difference in load and recovery.

A payment service is experiencing a partial outage (it's recovering slowly). 100 requests arrive per second that depend on it.
═══════════════════════════════════════════════════════════════
  WITHOUT A RETRY BUDGET (Naive — 5 immediate retries each)
═══════════════════════════════════════════════════════════════

  Time    Payment service load       What's happening
  ─────   ────────────────────       ─────────────────
  14:00   100 req/s                  100 requests fail
  14:00   +100 retries (1st)         100 more attempts
  14:00   +100 retries (2nd)         100 more attempts
  14:00   +100 retries (3rd)         100 more attempts
  14:00   +100 retries (4th)         100 more attempts
  14:00   +100 retries (5th)         100 more attempts
          ──────────────             ─────────────────────
          600 req/s hitting          The service is trying to
          a failing service          recover, but it's being
                                      SMOTHERED by 600 req/s.

  14:01   Now 500 requests fail      The extra load makes
          → 5 retries each =         recovery slower
          2500 req/s hitting it      → a death spiral

  ┌─────────────────────────────────────────────────────────┐
  │                                                         │
  │  ❌ RETRY STORM                                         │
  │  The service was recovering, but the retries overwhelmed│
  │  it. Recovery is DELAYED by the very system meant to    │
  │  help. Downstream load is 6x the original.              │
  │                                                         │
  └─────────────────────────────────────────────────────────┘

═══════════════════════════════════════════════════════════════
  WITH A RETRY BUDGET (Bounded — 3 retries, exponential backoff)
═══════════════════════════════════════════════════════════════

  Budget: 3 retries max, with exponential backoff:
          wait 100ms → 200ms → 400ms, then give up.

  Time    Payment service load       What's happening
  ─────   ────────────────────       ─────────────────
  14:00   100 req/s                  100 requests fail
    14:00   +100 retries (1st, +100ms) 100 spaced attempts
    14:00   +100 retries (2nd, +200ms) 100 spaced attempts
    14:00   +100 retries (3rd, +400ms) 100 spaced attempts
            ──────────────             ─────────────────────
            ~200-300 req/s peak        Manageable load. The
            (not 600!)                  service can actually
                                        recover because it's
                                        not being smothered.
  
    14:01   The remaining failures     Those that exhausted
            that exhausted budget      their budget FAIL FAST
            → return error to user     → surface the error,
                                        no more retries.
  
    ┌─────────────────────────────────────────────────────────┐
    │                                                         │
    │  ✅ SERVICE RECOVERS                                      │
    │  The bounded load lets the service recover.             │
    │  The exponential backoff spaces out retries.            │
    │  Failed operations give up gracefully.                  │
    │                                                         │
    └─────────────────────────────────────────────────────────┘

Keep reading