Retry Budget
Retry budget: a system-wide guard against retry storms.
Per-call retry (see retryWithExponentialBackoff and friends) is unaware of how much total retry volume the rest of the system is currently generating. The well-known failure mode: a downstream outage causes every caller to retry simultaneously, multiplying load on an already-struggling service and making the outage worse instead of better.
A RetryBudget caps total retry volume as a fraction of primary request volume (the same token-bucket throttling strategy gRPC and Finagle use): every successful, non-retry call deposits a small fraction of a token; every retry attempt withdraws one whole token. Once the budget is exhausted, further retries are refused outright (failing fast) until enough successful traffic replenishes it — this is what makes it self-healing: the budget recovers on its own as soon as the system is healthy enough to produce the successes needed to refill it, with no manual intervention or reset required.
Basic usage, sharing one budget across every caller of a given downstream:
val budget = RetryBudget.create() // one instance per downstream, shared across calls
val result = retryWithBudget(budget, retries = 5) {
callFlakyService()
}Using the primitive directly, to gate a custom retry loop:
if (budget.tryWithdraw()) {
// perform the retry
} else {
// budget exhausted -- fail fast instead of piling onto a struggling downstream
}Types
Factory for RetryBudget.
Functions
Current token balance, from 0.0 to RetryBudgetConfig.maxTokens.
Records a successful, non-retry call, depositing RetryBudgetConfig.tokenRatio tokens (capped at RetryBudgetConfig.maxTokens).
Attempts to withdraw one token for a retry attempt.