tolerate a concurrent delete during the post-write stat

This commit is contained in:
Nystik
2026-06-16 17:03:17 +02:00
parent 9619703a58
commit 97bcf4fde5
2 changed files with 24 additions and 2 deletions

View File

@@ -31,9 +31,19 @@ async function writeToDisk(absPath, data, encoding) {
);
lastWriteTime.set(absPath, Date.now());
const stat = await fs.promises.stat(absPath);
return { mtime: stat.mtimeMs, size: stat.size };
// A concurrent delete can remove the file between the write and the stat (a rapid write-then-delete on the same path).
// The write itself succeeds, so report synthetic metadata rather than failing the request on the now-missing file.
try {
const stat = await fs.promises.stat(absPath);
return { mtime: stat.mtimeMs, size: stat.size };
} catch (e) {
if (e.code === "ENOENT") {
return { mtime: Date.now(), size: estimateSize(data, encoding) };
}
throw e;
}
}
function flushEntry(absPath) {

View File

@@ -99,6 +99,18 @@ describe("writeCoalesced", () => {
expect(elapsed).toBeLessThan(20);
});
it("returns synthetic metadata when the file is deleted before the post-write stat", async () => {
const filePath = path.join(tmpDir, "race.txt");
vi.spyOn(fs.promises, "stat").mockRejectedValueOnce(
Object.assign(new Error("ENOENT"), { code: "ENOENT" }),
);
const result = await coalescer.writeCoalesced(filePath, "hello", "utf-8");
expect(result.size).toBe(5);
expect(result.mtime).toBeGreaterThan(0);
});
});
describe("getPending", () => {