relv-space4 / src /utils /retry.ts
relv-dev's picture
Deploy latest verified Turnitin worker
521a9b6 verified
Raw
History Blame Contribute Delete
1.41 kB
import { logger } from './logger';
interface RetryOptions {
maxAttempts: number;
baseDelayMs?: number;
maxDelayMs?: number;
retryIf?: (error: unknown) => boolean;
label?: string;
}
/**
* Retry a function with exponential backoff.
* Returns the result on success or throws after all attempts fail.
*/
export async function retry<T>(
fn: () => Promise<T>,
options: RetryOptions
): Promise<T> {
const {
maxAttempts,
baseDelayMs = 1000,
maxDelayMs = 30000,
retryIf,
label = 'operation',
} = options;
let lastError: unknown;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (retryIf && !retryIf(error)) {
throw error;
}
if (attempt === maxAttempts) {
break;
}
const delay = Math.min(baseDelayMs * Math.pow(2, attempt - 1), maxDelayMs);
const jitter = delay * (0.5 + Math.random() * 0.5);
logger.warn(`${label} attempt ${attempt}/${maxAttempts} failed, retrying in ${Math.round(jitter)}ms`, {
error: error instanceof Error ? error.message : String(error),
attempt,
maxAttempts,
});
await sleep(jitter);
}
}
throw lastError;
}
/** Promise-based sleep */
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}