mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-27 23:27:09 +09:00
90 lines
1.9 KiB
TypeScript
90 lines
1.9 KiB
TypeScript
/**
|
|
* Incremental backoff with decorrelated jitter.
|
|
*
|
|
* Produces progressively longer delays after consecutive failures,
|
|
* capped at a configurable maximum. Uses decorrelated jitter
|
|
* (delay = random(base, prev * multiplier)) to prevent thundering-herd
|
|
* when many clients retry simultaneously after a shared outage.
|
|
*
|
|
* The backoff is a pure, stateful object with no timers or side effects.
|
|
* Call `next()` to get the next delay, and `reset()` after a success.
|
|
*/
|
|
|
|
export type BackoffOptions = {
|
|
/**
|
|
* Base delay in milliseconds.
|
|
* @default 5_000
|
|
*/
|
|
baseDelay?: number;
|
|
|
|
/**
|
|
* Maximum delay in milliseconds.
|
|
* @default 120_000
|
|
*/
|
|
maxDelay?: number;
|
|
|
|
/**
|
|
* Growth factor applied to the previous delay on each failure.
|
|
* @default 3
|
|
*/
|
|
multiplier?: number;
|
|
|
|
/**
|
|
* When true, randomize delays between baseDelay and previousDelay * multiplier.
|
|
* @default true
|
|
*/
|
|
jitter?: boolean;
|
|
};
|
|
|
|
export type Backoff = {
|
|
next: () => number;
|
|
reset: () => void;
|
|
attempts: () => number;
|
|
};
|
|
|
|
const DEFAULT_BASE_DELAY = 5_000;
|
|
const DEFAULT_MAX_DELAY = 120_000;
|
|
const DEFAULT_MULTIPLIER = 3;
|
|
|
|
const randomBetween = (min: number, max: number) => {
|
|
return Math.random() * (max - min) + min;
|
|
};
|
|
|
|
export const createBackoff = (options?: BackoffOptions): Backoff => {
|
|
const {
|
|
baseDelay = DEFAULT_BASE_DELAY,
|
|
jitter = true,
|
|
maxDelay = DEFAULT_MAX_DELAY,
|
|
multiplier = DEFAULT_MULTIPLIER,
|
|
} = options ?? {};
|
|
|
|
let failures = 0;
|
|
let lastDelay = baseDelay;
|
|
|
|
const next = () => {
|
|
failures += 1;
|
|
|
|
if (jitter) {
|
|
lastDelay = Math.min(
|
|
maxDelay,
|
|
randomBetween(baseDelay, lastDelay * multiplier)
|
|
);
|
|
return lastDelay;
|
|
}
|
|
|
|
lastDelay = Math.min(maxDelay, baseDelay * multiplier ** (failures - 1));
|
|
return lastDelay;
|
|
};
|
|
|
|
const reset = () => {
|
|
failures = 0;
|
|
lastDelay = baseDelay;
|
|
};
|
|
|
|
return {
|
|
attempts: () => failures,
|
|
next,
|
|
reset,
|
|
};
|
|
};
|