writePositionMap: tmp + rename — the same crash-safety rewriteLedgers uses
Flag routed from the UI stream's rename-shuffle fix (a9339b3): the
positions ledger was the one remaining plain writeFileSync among the
rewritable ledgers — a torn write (crash mid-write) could replace it
with a half-written file, and readPositionMap parses a failure to {}
(honest empty, but the resting layout would be silently lost). The
tmp+rename pattern from rewriteLedgers, ~4 lines up the file: write a
uniquely-named temp, rename over the ledger — a crash leaves either the
old ledger intact or the new one complete, plus an orphan .tmp-*.
879/879 tests, typecheck/lint/build green; live-verified via the node
deletion's positions prune (no temp residue, valid JSON).
This commit is contained in:
+15
-5
@@ -304,11 +304,21 @@ function readPositionMap(): Record<string, Position> {
|
|||||||
|
|
||||||
function writePositionMap(positions: Record<string, Position>): void {
|
function writePositionMap(positions: Record<string, Position>): void {
|
||||||
fs.mkdirSync(path.dirname(POSITIONS_FILE), { recursive: true });
|
fs.mkdirSync(path.dirname(POSITIONS_FILE), { recursive: true });
|
||||||
fs.writeFileSync(
|
// Tmp + rename — the same crash-safety rewriteLedgers uses two blocks
|
||||||
POSITIONS_FILE,
|
// up (a9339b3 UI flag): a torn write must never replace the ledger
|
||||||
JSON.stringify({ positions, updatedAt: new Date().toISOString() }, null, "\t"),
|
// with a half-written file the reader parses as empty.
|
||||||
"utf-8",
|
const tmp = `${POSITIONS_FILE}.tmp-${crypto.randomUUID().slice(0, 8)}`;
|
||||||
);
|
try {
|
||||||
|
fs.writeFileSync(tmp, JSON.stringify({ positions, updatedAt: new Date().toISOString() }, null, "\t"), "utf-8");
|
||||||
|
fs.renameSync(tmp, POSITIONS_FILE);
|
||||||
|
} catch (err) {
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(tmp);
|
||||||
|
} catch {
|
||||||
|
/* the rename either already happened or the tmp never existed */
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPosition = (v: unknown): v is Position =>
|
const isPosition = (v: unknown): v is Position =>
|
||||||
|
|||||||
Reference in New Issue
Block a user