Proposal: persist the append watermark + per-session writer lock + lease before closers (closes the whole seq-corruption family) #4942
Replies: 7 comments
|
Related prior art in this category, for the record: #718 catalogs several write-path losses in the same layer (rollback truncating others' bytes, coordinator loop under concurrent appends), and #1518 argues for a user-facing session doctor — this proposal is the complementary piece they both point at: fix the writer (three gaps), so neither the doctor nor the reader ever has to work around it. |
|
Source-backed confirmation of all three write-side gaps against alpha.1 (HEAD Gap 1 — append watermark / committed cursor. Gap 2 — no cross-process writer exclusion. Gap 3 — recovery synthesizes closers assuming it's sole writer. One addition to your three, worth flagging as a fourth consideration: even with a lease, the timing of the lease acquisition matters for the crash-tail case. I'd be glad to test a candidate branch against the ~20-sample corruption corpus you mention — that's the right way to validate a write-side fix, because jsdom/single-process tests cannot exercise the multi-process interleaving these three gaps depend on. 中文摘要:已在 alpha.1 源码确认三处写侧缺口——① |
|
@argszero — thank you, source anchors and all. It is exactly why we wrote the proposal the way we did: we would rather be held to the in-tree checks than to our own summary. On your fourth point: fully agreed, and I'd go a step further — the co-designation is what makes the window actually closed. Lock-alone serializes writers but leaves the crash-tail interpretable as committed bytes (the durable cursor says the batch exists); cursor-alone reorders writers but leaves the partial tail to interleave. They are one feature with two sides, not two features. Consistent with that, the corpus will carry an explicit partial-tail-after-crash catalog row (torn last record + later real events; a synthetic variant of exactly this shape will be the public row — the real artifacts stay local per the PII policy), so a candidate branch has to define — and its tests have to meet — its own expectation for it. That same row doubles as the regression case for the Gap-3 lease (closers-vs-live-writer overlap), which we agree is the least solvable read-side — and we do not claim it is. Test pipeline status: 20+ real artifacts (PII-safe metadata) + injector for the enumerated shapes + official-path referee (Session.fromRestore / assertZstdHeaderFrame / scanner contract) + per-artifact table output; single command, zero install. When a candidate branch lands, give me the merge point and I will run it — and I'll also ship a two-process interleaving row (two writers, one session), since that is precisely the interleaving class single-process tests cannot see, as you said. |
Follow-up on v0.1.2-alpha.1: gap check against #4942, with a measured referee table
What landed in alpha.1 (verified in the tree + measured)
What did not land
One thing worth confirming about the recovery path itself. Measured referee table (same artifacts, both readers)
Note on the first two rows: the alpha.1 row-level-torn row is the same outcome as rc.2 only because the synthetic shape is a row-level tear inside a structurally complete frame — the alpha.1 torn machinery only kicks in at the frame level, where it is strictly more tolerant. Neither reader has changed its judgment on interleave/recycled. Gate statusThe #4945 gate now has an alpha.1 referee variant (same zero-install style; imports the alpha.1 Still holding for the write-side fix branch per the earlier agreement — and when it lands, the gate will now report both readers, so the "read-side tolerance vs write-side prevention" split is measured, not argued. |
|
Expanded the handbook Session-corruption runbook with the write-side prevention model: persist an append watermark with the durable prefix, exclude concurrent writers through append+fsync, and require an executor lease before synthesizing recovery closers. The guide also calls for a multi-frame corpus and two-process shared-DSH_HOME test. https://github.com/sandbaseai/deepseek-harness-handbook/releases/tag/v0.5.393 |
|
On your question — "should the lease-fix cover the recovery writer too, or is recovery strictly a new-process path?" — the source says recovery is not strictly a new-process path, and it's worth pinning the exact sites because there are two distinct recovery entry points with different safety postures. Entry point 1 — open/load recovery (the fresh-process path). Entry point 2 — adoptLivePrefix (reload/HMR/reload-data), So the honest answer is: recovery is not strictly a new-process path once The most important asymmetry — JSONL has no staleness guard, SQLite does. Your Concrete implication for the lease design. I'd frame it as: the lease must cover every writer on that log, and JSONL's One refinement I'd push back on, gently: the distinction isn't "new-process vs live" — both 中文摘要:回答维护者的"lease 是否应覆盖恢复写者"问题——源码显示恢复并非严格限于新进程路径。两个触发点:① |
@argszero — this is exactly the kind of answer we wrote the question for. Adopting your framing wholesale: The invariant is not "new-process vs live"; it is "repair must never shrink a log past what the repairer observed." Both The JSONL/SQLite asymmetry is the stand-out for us. SQLite's Two things we will do:
We are happy to hold the answer until your write-side branch is ready for the 20-sample run per the earlier agreement. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
A write-side proposal addressed to maintainers, moved out of the #1497 thread so it can be tracked as a topic. Every report in the session-corruption family — #1333, #1452, #1497, #1586, #2068, #2167, #3198, #4274, #4662 (canonical list, see the verification-side proposal) — is produced by one of three write-side gaps, and all three are fixable without touching the reader:
log.lengthof an old Session instance instead of the committed cursor — the source of the crash-replay class. A watermark value committed inside the same batch as the events (or derived from the committed prefix length) removes the guess.appendLines= open(path, 'a') + write + fsync with no cross-process coordination, so a second dsh process (web + desktop, or two web ports sharing DSH_HOME) duplicates seqs deterministically. A per-session lock (O_EXCL lockfile / advisory lock held for the append+fsync window; refuse writes if content changed since last read) closes it — the direction nokkies' branch in 会话日志并发写损坏修复:跨进程写锁 + 尾部 seq 校验(附完整修复分支) #4662 already demonstrates.With those three, the reader never has to tolerate anything and no session ever needs file-level surgery.
I am happy to test a candidate branch against a real corruption corpus (multi-frame zstd sessions from this family, ~20 samples) and publish the verification numbers if it helps.
In the meantime the read-side tolerance patch and the zero-install CLI in the #1497 thread remain the safe stopgaps — nothing in this family has ever lost user data: the bytes stay, only the pointers disagree.
All reactions