Repository navigation
v1.12.11 - a run whose lock was taken over stops before its next write
A run whose lock was taken over stops before its next write
A wash, RE-TIER or ULTRA run whose lock went stale and was taken over by another run now stops before its next write instead of writing on beside the new holder. Nothing is removed from the plugin and no command or config key changes; a refusal gains additive fields (robbed, committed and lockLost, plus lockLeft when a lock had to be left behind, which also rides a finished wash, RE-TIER or ULTRA result and a thrown error). The installed plugin moved in four files (git diff --stat 904e783 HEAD -- plugin/: 4 files, 262 insertions, 45 deletions): the plugin/ twins of scripts/lib/apply.mjs, scripts/lib/retier.mjs and scripts/lib/estate-archive.mjs (git diff --stat 904e783 HEAD -- plugin/scripts/lib/: 3 files, 261 insertions, 44 deletions), and the version in plugin.json.
Fixed
- A run whose lock was taken over after it went stale no longer writes beside the new holder. A lock older than
LOCK_STALE_MS(30 minutes) is presumed dead and another run may take it over; a run that had only stalled (a suspended machine, a long pause) then woke and kept writing, and its rollback could overwrite what the new holder had written since. Now the wash transaction (applyPlan'sfence), RE-TIER (runRetier'sstolen) and the ULTRA estate run (archiveSession's newlockStateoption) each re-read their lock immediately before every write step: one bounded read per step, none inside a byte, line or content loop. A run robbed of its lock returns a refusal (withrobbedandlockLost: 'stolen') that startslock stolen: another CoalWash run took over the <project|global> lockand says what it left on disk. It does not roll back, because a restore would overwrite the new holder's work. RE-TIER keeps every archive copy it wrote, and an ULTRA failure after a theft removes no partial copy, since the new holder may be archiving to the same paths. Residuals: a theft landing between the lock read and the write it guards still goes through (a filesystem cannot check a token); the new holder overwrites the robbed run's single journal, after which that run's record lives in its refusal and its snapshot (kept for the newest 3). — test:scripts/lib/apply.test.mjs,scripts/lib/retier.test.mjsandscripts/lib/estate-archive.test.mjs, the tests namedfire 119: ...(for examplefire 119: every fence of a committing transaction stops a robbed run on the spot (15 fences, one theft each)andfire 119: an archive failure after a theft removes nothing: the partial copies stay (the new holder may own copies at those paths)) - A lock file another program holds open for a moment is no longer reported as a takeover by another CoalWash run. An indexer, antivirus or sync client can hold the lock file open for milliseconds, so a read of it fails. A failed read is now retried (
LOCK_READ_RETRIES8,LOCK_READ_WAIT_MS125: about one second); a read that succeeds is final, and a missing or foreign token is a theft as before. A lock still unreadable after that stops the run with its own refusal,lock unreadable: ... could not read its own <project|global> lock file ..., withlockLost: 'unreadable'(androbbed, which RE-TIER and ULTRA use to keep their copies). It says what landed; the journal on disk is still that run's, so the next gauge's dangling-transaction recovery rolls the store back from the snapshot, which undoes the wash even when every step had landed. The release retries the same way, and a release that cannot read or remove the lock never deletes a lock it cannot prove is its own: it leaves it in place and says so inlockLeft(the note is appended to a refusal's error, listed inflaggedon a finished wash, printed asLOCK LEFT:in the RE-TIER and ULTRA reports, and carried on a thrown error ase.lockLeft). A later run defers on that lock until it goes stale (30 minutes), or the user removes it once no CoalWash run is active. A lock file that cannot be read back right after it is created defers with that reason instead of "lost a race". — test:scripts/lib/apply.test.mjs,scripts/lib/retier.test.mjsandscripts/lib/estate-archive.test.mjs, the tests namedfire 122: ...andfire 123: ...(for examplefire 122: a lock file unreadable for a moment mid-run (EBUSY for 3 reads) is retried: the run finishes, releases its lock and reports no theftandfire 123: an unexpected error inside the transaction still names a lock the release had to leave behind)