Spaces:
Sleeping
Sleeping
File size: 1,413 Bytes
2db5489 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | 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));
}
|