41 lines
1.1 KiB
TypeScript
41 lines
1.1 KiB
TypeScript
export interface FilesystemRetryOptions {
|
|
retryDelaysMs?: readonly number[];
|
|
wait?: (delayMs: number) => void;
|
|
}
|
|
|
|
const DEFAULT_RETRY_DELAYS_MS = [100, 300, 900] as const;
|
|
const TRANSIENT_FILESYSTEM_ERROR_CODES = new Set([
|
|
'EIO',
|
|
'EPERM',
|
|
'EBUSY',
|
|
'EACCES',
|
|
]);
|
|
|
|
function waitSynchronously(delayMs: number): void {
|
|
const signal = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
Atomics.wait(signal, 0, 0, delayMs);
|
|
}
|
|
|
|
export function retryTransientFilesystemOperation<T>(
|
|
operation: () => T,
|
|
options: FilesystemRetryOptions = {},
|
|
): T {
|
|
const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
|
|
const wait = options.wait ?? waitSynchronously;
|
|
|
|
for (let attempt = 0; ; attempt += 1) {
|
|
try {
|
|
return operation();
|
|
} catch (error) {
|
|
const code = error && typeof error === 'object' && 'code' in error
|
|
? String(error.code)
|
|
: '';
|
|
const delayMs = retryDelaysMs[attempt];
|
|
if (!TRANSIENT_FILESYSTEM_ERROR_CODES.has(code) || delayMs === undefined) {
|
|
throw error;
|
|
}
|
|
wait(delayMs);
|
|
}
|
|
}
|
|
}
|