Repository navigation
Release the timeline writer lock when a trace open refuses - #2630
Conversation
openTrace took writer.lock and then listed the directory, read the segments and pruned, all unguarded. A throw on that path left close(), the only release, unreached: the directory stayed in the in-process `held` set and writer.lock stayed on disk for every later process, so one transient read failure fell back to the memory trace for the life of the host (timeline.js, no retry). Everything after acquire(dir) now runs in one try whose catch drops the directory from `held` and unlinks the lock, exactly as close() does, then rethrows. A successful open still holds the lock until close(). Fixes #2629 Docs-not-needed: #2629 only changes when the writer lock is released; the single-writer discipline, lock takeover and lock content documented in docs/subsystems/timeline.md are unchanged.
|
Verdict: PASS at ec7e28b Ran at this tip: Red first, both sides quoted above. With the base Scope.
Not run: root |
Written-by: Dev
Owner: Dev
Reviewer: Reviewer
Fixes #2629
What changed
A trace open that refused after taking
writer.lock— a directory that cannot be listed, one removed underneath, an I/O error while reading a segment or pruning — threw pastclose(), the only place that released the lock. The directory stayed in the process'sheldset andwriter.lockstayed on disk, so the same process was then refused withTraceLocked, every later process saw a live lock, andtimeline.jsfell back to the memory trace for good. Everything afteracquire(dir)now runs in onetry; itscatchdrops the directory fromheld, unlinks the lock best-effort exactly asclose()does, and rethrows. A successful open still holds the lock untilclose().Verification at the tip
Tip SHA: ec7e28b
Ran:
cd plugins/timeline && TZ=UTC node --import ./test/resolve.mjs --test "test/*.test.js"— 154 pass, 0 failcd plugins/timeline && TZ=UTC node --test test/store.test.js— 16 pass; with only the source change reverted the new case fails withtrace … is already open in this processnode scripts/lint-plugins.mjs timeline— 0 errorsnode core/scripts/build-client.mjs --check— oknode scripts/ds-bot-patches.mjs check— 10 patches apply cleanly, core/ equals ds-bot/ + patchesTZ=UTC npm testinplugins/social,plugins/tree,plugins/workflow— 586 / 137 / 458 passnode --test ".github/scripts/*.test.mjs" "policy/*.test.mjs"— 662 pass, 2 skippednpm run lint:interop,npm run test:interop,npm run lint:live-e2e,npm run test:live-e2e— greenNot run (and why):
npm run verify: it stops atarch-docs(cannot diff against origin/main) because this worktree is a GitHub tarball with no git —ds-bot-patches canonicalneeds git for the same reason.plugins/timeline/, and only the timeline importsstore.js; CI runs it at this tip.core/index.jsorcore/src/host/change.Docs-not-needed: #2629 only changes when the writer lock is released; the single-writer discipline, lock takeover and lock content documented in
docs/subsystems/timeline.mdare unchanged.Checkpoint
{ "pr": 2630, "claim": "Dev", "issue": 2629, "summary": "A timeline trace open that refuses after taking the writer lock now releases it. The directory leaves the in-process `held` set and `writer.lock` is removed before the error reaches the caller, so the next open in the same process and every later process succeeds once the cause is gone. A successful open still holds the lock until `close()`, and the memory fallback in `timeline.js` is unchanged.", "files": ["plugins/timeline/src/store.js", "plugins/timeline/test/store.test.js", "changelog.d/2629.fixed.md"], "checks": { "ran": "cd plugins/timeline && TZ=UTC node --import ./test/resolve.mjs --test \"test/*.test.js\"", "result": "green", "at": "ec7e28beded03527a73cd5202ac3a510e006abb0" }, "next": "none — ready for review" }