durable-utils
Version:
Utilities for Cloudflare Durable Objects and Workers, including SQL migrations, sharding and retry utilities, and more.
64 lines (63 loc) • 1.7 kB
JavaScript
// src/retries.ts
function jitterBackoff(attempt, baseDelayMs, maxDelayMs) {
const attemptUpperBoundMs = Math.min(2 ** attempt * baseDelayMs, maxDelayMs);
return Math.floor(Math.random() * attemptUpperBoundMs);
}
async function tryN(n, fn, optionsOrFunction, optionsOld) {
if (n <= 0) {
throw new Error("n must be greater than 0");
}
n = Math.floor(n);
let options;
if (typeof optionsOrFunction !== "function") {
options = optionsOrFunction ?? {};
} else {
options = {
...optionsOld,
isRetryable: optionsOrFunction
};
}
return await tryWhile(
fn,
(err, nextAttempt) => {
return nextAttempt <= n && (options.isRetryable?.(err, nextAttempt) ?? true);
},
options
);
}
async function tryWhile(fn, isRetryable, options) {
const baseDelayMs = Math.floor(options?.baseDelayMs ?? 100);
const maxDelayMs = Math.floor(options?.maxDelayMs ?? 3e3);
if (baseDelayMs <= 0 || maxDelayMs <= 0) {
throw new Error("baseDelayMs and maxDelayMs must be greater than 0");
}
if (baseDelayMs >= maxDelayMs) {
throw new Error("baseDelayMs must be less than maxDelayMs");
}
let attempt = 1;
while (true) {
try {
return await fn(attempt);
} catch (err) {
if (options?.verbose) {
console.info({
message: "tryWhile",
attempt,
error: String(err),
errorProps: err
});
}
attempt += 1;
if (!isRetryable(err, attempt)) {
throw err;
}
const delay = jitterBackoff(attempt, baseDelayMs, maxDelayMs);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}
export {
jitterBackoff,
tryN,
tryWhile
};