Exponential backoff with jitter for retries
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 5.4k
- Forks
- 195
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Add a new retryBackoff option that lets users configure exponential backoff with jitter for automatic retries, following the strategies described in the AWS Architecture Blog post "Exponential Backoff And Jitter" by Marc Brooker.
Motivation
The current retry mechanism (src/fetch.ts) only supports a fixed retryDelay (a number or a callback returning a number). This is fine for a single client, but it scales poorly when many clients fail simultaneously:
- With no delay (
retryDelay: 0, the default), every client retries immediately and stampedes the server while it is still recovering. - With a fixed delay (e.g.
500ms), all failed clients still retry in lockstep. the herd is preserved, just shifted in time. The server sees the same pulsing load pattern.
This is the classic "Thundering Herd" problem. The AWS post shows via simulation that adding exponential backoff alone is not enough; the key insight is that randomizing each client's retry schedule (jitter) is what actually decorrelates the herd. With jitter, both the peak load on the server and the overall completion time across all clients improve significantly.
Today, users who want this behavior have to implement it themselves inside a retryDelay callback, which is error-prone (need to track attempt count, pick the right formula, manage prev_sleep for decorrelated jitter, etc.). It would be much nicer if ofetch provided this out of the box.
Current behavior
// src/types.ts
retry?: number | false;
retryDelay?: number | ((context: FetchContext<T, R>) => number);
retryStatusCodes?: number[];
// src/fetch.ts (onError)
const retryDelay =
typeof context.options.retryDelay === "function"
? context.options.retryDelay(context)
: context.options.retryDelay || 0;
The FetchContext does not expose the current attempt number, so even a user-defined callback cannot easily implement backoff without external state.
Proposal
Introduce a new opt-in option retryBackoff that selects a backoff strategy and supplies base / cap delays in milliseconds:
await ofetch("/api", {
retry: 5,
retryBackoff: {
strategy: "full-jitter", // "full-jitter" | "equal-jitter" | "decorrelated-jitter"
base: 100, // minimum delay (ms)
cap: 3000, // maximum delay (ms)
},
});
Strategies
All three strategies from the AWS post would be supported so users can pick the one that matches their constraints:
-
full-jitter(recommended in the AWS post):
sleep = random(0, min(cap, base * 2 ** attempt))
Best overall — lowest server load and lowest completion time in the simulation. No state required. -
equal-jitter:
temp = min(cap, base * 2 ** attempt); sleep = temp / 2 + random(0, temp / 2)
Guarantees a minimum wait time (temp / 2), useful when "retry immediately" is undesirable. -
decorrelated-jitter:
sleep = min(cap, random(base, prev_sleep * 3))(withprev_sleep = baseon the first retry)
Comparable to full-jitter in performance and matches the AWS SDK's default. Requires tracking the previous sleep value.
Expose attempt count on the context
To keep callback-based use cases working and to let users observe retries, add a public retryAttempt?: number field to FetchContext:
undefinedon the first attempt1,2, ... on subsequent retries
Coexistence with retryDelay
When both retryBackoff and retryDelay are set, retryBackoff takes precedence and retryDelay is ignored. Documented explicitly to avoid silent mixing of the two semantics.
Backward compatibility
Fully opt-in. If retryBackoff is not specified, the existing retryDelay behavior is preserved exactly. No default delay is applied, no public type changes are breaking.
References
- AWS Architecture Blog: Exponential Backoff And Jitter Marc Brooker
- Existing retry code:
src/fetch.ts(onError),src/types.ts(retry options),README.md("Auto Retry" section)
Additional information
- Would you be willing to help implement this feature?
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the retry handling in src/fetch.ts, the retry option types in src/types.ts, and the “Auto Retry” section of README.md. Trace how attempts and retryDelay are currently handled, then define the three proposed backoff strategies, their precedence, context field, documentation, and opt-in backward-compatible behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 62/100