Hedging cuts tail latency. If the first attempt hasn’t answered after a delay, it starts a second one without cancelling the first, and keeps whichever good result arrives first. The losers are aborted.
Use it for idempotent reads whose latency has a long tail: a replica that’s sometimes slow, a search query, a cache cluster. Don’t use it for writes, or when the extra load would hurt a struggling dependency; every hedge is another request. Unlike retry, it doesn’t wait for a failure before trying again.
Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), …
Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with
retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.
The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().
Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), …
Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with
retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.
The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().
ms to wait before starting another attempt. Default 2000.
> 0 latency mode, 0 parallel mode (all at once), < 0 fallback mode (next attempt only after a failure).
delay: '150ms', // ≈ your p95: start a second attempt if the first is slower
HedgingOptions<string[]>.maxHedgedAttempts?: number |undefined
Hedged attempts in addition to the primary. Default 1.
ResiliencePipelineBuilder adds Polly-style addRetry() / addTimeout() / … shortcuts on top, at the cost of
bundling every strategy. Strategies run in the order they are added: the first one added is the outermost.
Options are validated and strategy state (e.g. circuit breaker) is created on build().
ResiliencePipelineBuilder adds Polly-style addRetry() / addTimeout() / … shortcuts on top, at the cost of
bundling every strategy. Strategies run in the order they are added: the first one added is the outermost.
Options are validated and strategy state (e.g. circuit breaker) is created on build().
ms to wait before starting another attempt. Default 2000.
> 0 latency mode, 0 parallel mode (all at once), < 0 fallback mode (next attempt only after a failure).
delay: '150ms',
HedgingOptions<T>.maxHedgedAttempts?: number |undefined
Hedged attempts in addition to the primary. Default 1.
The primary attempt starts (context.attemptNumber 0).
While fewer than 1 + maxHedgedAttempts attempts have started, the strategy waits up to the delay for any attempt to finish. If none does, it starts the next one and reports OnHedging.
When an attempt finishes:
If shouldHandle doesn’t match it (a success, or an error that isn’t transient, such as a 404), it wins. The other attempts are aborted and the outcome is returned right away; hedging doesn’t wait for the losers to settle.
If it’s a handled failure, the next attempt starts at once (if any are left), and the others keep running.
When every attempt has failed, the primary’s failure is returned.
The default shouldHandle is the same as retry’s: errors except 4xx (408/429 count), aborts and rejections. Results never trigger a hedge unless your predicate says so.
signal, linked to the caller’s. Aborting the execution aborts all attempts; a winner aborts only the others.
attemptNumber: 0 for the primary, then 1, 2, … in the order attempts start. One counter covers the whole execution, so with a retry around hedging or inside the hedged attempts every attempt still gets a distinct number, safe for per-attempt idempotency keys. (onHedging, delayGenerator and actionGenerator receive hedging’s own count: 1 for the first hedge.)
copy of properties. Parallel attempts can’t overwrite each other’s values. When an attempt wins, its properties are merged back into the caller’s map. Losers’ writes are dropped.
Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), …
Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with
retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.
The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().
ms to wait before starting another attempt. Default 2000.
> 0 latency mode, 0 parallel mode (all at once), < 0 fallback mode (next attempt only after a failure).
delay: 0,
HedgingOptions<string>.maxHedgedAttempts?: number |undefined
Hedged attempts in addition to the primary. Default 1.
Runs the callback through the pipeline; throws the final error.
execute(
({
signal: AbortSignal
Aborted when the caller cancels, or when a strategy (timeout, hedging) gives up on this execution.
signal,
attemptNumber: number
0 for the first attempt, then 1, 2, … in the order attempts start. Use it for logging or per-attempt idempotency keys.
One counter per execution numbers every retry and hedged attempt, however retry and hedging are nested
(retry around hedging, retry inside hedged branches), so a number is never repeated within an execution.
Strategies' own counters (RetryPredicateArgs.attemptNumber, OnHedgingArgs.attemptNumber) stay local.
attemptNumber,
properties: Map<string, unknown>
Shared bag for passing data between strategies and the callback.
properties }) => {
properties: Map<string, unknown>
Shared bag for passing data between strategies and the callback.
Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated.
set('replica',
attemptNumber: number
0 for the first attempt, then 1, 2, … in the order attempts start. Use it for logging or per-attempt idempotency keys.
One counter per execution numbers every retry and hedged attempt, however retry and hedging are nested
(retry around hedging, retry inside hedged branches), so a number is never repeated within an execution.
Strategies' own counters (RetryPredicateArgs.attemptNumber, OnHedgingArgs.attemptNumber) stay local.
0 for the first attempt, then 1, 2, … in the order attempts start. Use it for logging or per-attempt idempotency keys.
One counter per execution numbers every retry and hedged attempt, however retry and hedging are nested
(retry around hedging, retry inside hedged branches), so a number is never repeated within an execution.
Strategies' own counters (RetryPredicateArgs.attemptNumber, OnHedgingArgs.attemptNumber) stay local.
attemptNumber,
signal: AbortSignal
Aborted when the caller cancels, or when a strategy (timeout, hedging) gives up on this execution.
Returns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map.
@returns ― Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned.
actionGenerator returns the action for a hedged attempt, for example another region or endpoint. Return undefined to repeat the original callback. It isn’t called for the primary.
Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), …
Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with
retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.
The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().
ResiliencePipelineBuilder<
interfaceResponse
The Response interface of the Fetch API represents the response to a request.
ms to wait before starting another attempt. Default 2000.
> 0 latency mode, 0 parallel mode (all at once), < 0 fallback mode (next attempt only after a failure).
delay: -1, // fallback mode: next region only after a failure
HedgingOptions<Response>.maxHedgedAttempts?: number |undefined
Hedged attempts in addition to the primary. Default 1.
The generated action replaces the strategies added after hedging as well as the callback. To keep them, call the callback the generator receives: it runs the rest of the pipeline with the original callback, and throws on failure.
delayGenerator returns the delay before each next attempt. It gets { attemptNumber, context }, where attemptNumber is the attempt about to start (1 for the first hedge). The sign rules above apply per step: return -1 to wait for a failure.
// First hedge after 100 ms, the next one only if something fails.
Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), …
Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with
retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.
The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().
With execute(fn, { idempotent: false }), hedging never starts an attempt while another one may be in flight. After a failure it only starts another if the failed attempt never started, i.e. it was a rejection, and only if your shouldHandle handles rejections. With the default predicate, a non-idempotent call runs exactly once.
Every call of the original callback, emitted by the pipeline. Actions from actionGenerator that don’t call callback aren’t counted.
Hedging raises no error of its own. When every attempt fails, the primary attempt’s error is returned. Losers are aborted with a plain abort(), so their signal’s reason is an AbortError. They are also aborted when hedging itself fails (an actionGenerator, onHedging or delayGenerator that throws). With executeStream, a loser that already produced its first chunk has its stream closed, and so does a stream your shouldHandle rejected (unless it ends up being the returned result).
Not passing signal. The losers keep running and their responses are thrown away.
Timeout outside hedging when you meant per attempt.addTimeout().addHedging() limits the whole race; addHedging().addTimeout() limits each attempt. Both are useful; pick on purpose.
Hedging writes. Use idempotent: false for them, or don’t hedge them.