POST writes the keeper pack-log + idx as the FIRST store mutation
(post.js:242-243), THEN advances the ref with an expected-old CAS
(post.js:246 → advanceRef, post.js:112-118). When the CAS loses — a
concurrent advance, exactly the race the CAS defends — advanceRef throws
POSTNOFF (post.js:114-116) AFTER the pack is on disk. There is no
rollback and no GC path anywhere, so each lost-CAS post leaves the
just-written pack-log + idx orphaned: a permanent per-incident disk leak.
Content is content-addressed, so it is a leak, not corruption. Method:
Issues.
post.js:242-243: commitM.writePack(reader.shard, info.wt, …) —
"the FIRST store mutation" (the comment :240 says so).post.js:246: advanceRef(reader, reader.shard, branchKey,
expectedOld, commit.sha) runs AFTER the pack write.post.js:112-118 advanceRef: if ((cur||"") !== (expectedOld||""))
throw "POSTNOFF: REFS … advanced concurrently — retry" — the lost CAS.NNNNNNNNNN.keeper(+.idx) just written stays on disk forever.writePack byte-output unchanged.writePack (re-check expected-old,
then write, then commit the ref).advanceRef in a catch that unlinks the freshly written
pack-log + idx before re-throwing POSTNOFF (record their paths from
writePack).test/js/post/*: force a lost CAS (mutate the ref between
pre-flight and advanceRef) and assert NO orphan pack-log/idx remains.writePack must surface the exact file names it created so a catch can
unlink precisely (avoid clobbering a concurrent winner's pack).verbs/post/post.js:685
commitM.writePack(reader.shard,…) runs BEFORE :692 advanceRef(…); no catch/unlink,
no GC. A lost CAS (advanceRef:222-225 POSTNOFF) leaves the pack-log+idx orphaned.