mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
fix(File Renames): Retry renames for windows EPERM issue (#7645)
* Retry renames for windows EPERM issue * use async version and manage temporary errors only * windows only rename magic
This commit is contained in:
parent
9384139867
commit
53421aa72e
@ -2,6 +2,7 @@ import fs from 'fs/promises';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import type { BaseDriver } from './base';
|
import type { BaseDriver } from './base';
|
||||||
|
import { gracefulRename } from './graceful-rename';
|
||||||
|
|
||||||
export default class FileSystemDriver implements BaseDriver {
|
export default class FileSystemDriver implements BaseDriver {
|
||||||
_directory: string;
|
_directory: string;
|
||||||
@ -43,7 +44,7 @@ export default class FileSystemDriver implements BaseDriver {
|
|||||||
// file (non-atomic) then renaming the file to the final value (atomic)
|
// file (non-atomic) then renaming the file to the final value (atomic)
|
||||||
try {
|
try {
|
||||||
await fs.writeFile(tmpPath, value, 'utf8');
|
await fs.writeFile(tmpPath, value, 'utf8');
|
||||||
await fs.rename(tmpPath, finalPath);
|
await gracefulRename(tmpPath, finalPath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[FileSystemDriver] Failed to write to ${tmpPath} then rename to ${finalPath}`, err);
|
console.error(`[FileSystemDriver] Failed to write to ${tmpPath} then rename to ${finalPath}`, err);
|
||||||
throw err;
|
throw err;
|
||||||
|
64
packages/insomnia/src/sync/store/drivers/graceful-rename.ts
Normal file
64
packages/insomnia/src/sync/store/drivers/graceful-rename.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import fs from 'fs/promises';
|
||||||
|
|
||||||
|
import { isWindows } from '../../../common/constants';
|
||||||
|
// Based on node-graceful-fs and vs-code's take on renaming files in a way that is more resilient to Windows locking renames
|
||||||
|
// https://github.com/microsoft/vscode/pull/188899/files#diff-2bf233effbb62ea789bb7c4739d222a43ccd97ed9f1219f75bb07e9dee91c1a7R529
|
||||||
|
// On Windows, A/V software can lock the directory, causing this
|
||||||
|
// to fail with an EACCES or EPERM if the directory contains newly
|
||||||
|
// created files.
|
||||||
|
|
||||||
|
const WINDOWS_RENAME_TIMEOUT = 60000; // 1 minute
|
||||||
|
|
||||||
|
function wait(ms: number): Promise<void> {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renameWithRetry(source: string, target: string, startTime: number, retryTimeout: number, attempt = 0): Promise<void> {
|
||||||
|
try {
|
||||||
|
return await fs.rename(source, target);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== 'EACCES' && error.code !== 'EPERM' && error.code !== 'EBUSY') {
|
||||||
|
// only for errors we think are temporary
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Date.now() - startTime >= retryTimeout) {
|
||||||
|
console.error(`[node.js fs] rename failed after ${attempt} retries with error: ${error}`);
|
||||||
|
// give up after configurable timeout
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt === 0) {
|
||||||
|
let abortRetry = false;
|
||||||
|
try {
|
||||||
|
const stat = await fs.stat(target);
|
||||||
|
if (!stat.isFile()) {
|
||||||
|
abortRetry = true; // if target is not a file, EPERM error may be raised and we should not attempt to retry
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abortRetry) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay with incremental backoff up to 100ms
|
||||||
|
await wait(Math.min(100, attempt * 10));
|
||||||
|
|
||||||
|
// Attempt again
|
||||||
|
return renameWithRetry(source, target, startTime, retryTimeout, attempt + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function gracefulRename(
|
||||||
|
from: string,
|
||||||
|
to: string,
|
||||||
|
) {
|
||||||
|
if (isWindows()) {
|
||||||
|
return renameWithRetry(from, to, Date.now(), WINDOWS_RENAME_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.rename(from, to);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user