fix(core): mitigate Windows EPERM flakiness in project registry saves

Introduces a retry mechanism for fs.promises.rename when saving the project registry. This resolves a known concurrency issue on Windows CI runners where multiple processes spinning up simultaneously during E2E tests cause file lock contention.
This commit is contained in:
Mahima Shanware
2026-04-09 20:16:43 +00:00
parent c29ab1afb8
commit 28c5fe7b28
+24 -11
View File
@@ -83,17 +83,30 @@ export class ProjectRegistry {
await fs.promises.mkdir(dir, { recursive: true });
}
try {
const content = JSON.stringify(data, null, 2);
// Use a randomized tmp path to avoid ENOENT crashes when save() is called concurrently
const tmpPath = this.registryPath + '.' + randomUUID() + '.tmp';
await fs.promises.writeFile(tmpPath, content, 'utf8');
await fs.promises.rename(tmpPath, this.registryPath);
} catch (error) {
debugLogger.error(
`Failed to save project registry to ${this.registryPath}:`,
error,
);
let attempt = 0;
const maxAttempts = 5;
const retryDelayMs = 100;
while (attempt < maxAttempts) {
try {
const content = JSON.stringify(data, null, 2);
// Use a randomized tmp path to avoid ENOENT crashes when save() is called concurrently
const tmpPath = this.registryPath + '.' + randomUUID() + '.tmp';
await fs.promises.writeFile(tmpPath, content, 'utf8');
await fs.promises.rename(tmpPath, this.registryPath);
return; // Success
} catch (error) {
attempt++;
if (attempt >= maxAttempts) {
debugLogger.error(
`Failed to save project registry to ${this.registryPath} after ${maxAttempts} attempts:`,
error,
);
} else {
// Wait before retrying (exponential backoff could be used here too)
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
}
}
}
}