Replies: 1 comment
|
Following the ecosystem-contribution route (external PRs aren't accepted yet), this report now has a companion community plugin: dsh-single-instance-guard — https://github.com/Tang-mm95/dsh-single-instance-guard A zero-dependency cordis plugin that takes an exclusive |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Symptom
A session that previously loaded fine starts failing to open with:
The error comes from
@deepseek-ai/dsh-session-persistence-jsonl's reader, which requires the decoded event stream to be strictly contiguous (events[i].seq === i). Once this fires, the whole session becomes unreadable — in every client (web UI, desktop wrappers) because they all read the same file underDSH_HOME.Root cause: concurrent writers on one session log
The JSONL backend documents "One live writer per session" — append coordination exists only inside a single process. The file format assumes it: each append batch is a compressed frame appended at the writer's cached EOF offset, and packed chunk rows carry a
seq0cursor computed from that writer's in-memory view.When two
dshserver processes share oneDSH_HOME(e.g. a desktop wrapper spawning its own server while another server already serves the same data root, or twodsh webprocesses in different terminals), the second writer's cursor is stale. Its next batch lands at an offset that overlaps the first writer's already-written rows. The result is not a torn frame (crash recovery handles those) but a structurally valid file with overlapping sequences — exactly what the committed-region check rejects.Decoded rows at the corruption point (real capture):
The stale
seq0(off by exactly the two rows writer A appended) is the signature of the race.This is not specific to any desktop wrapper: two plain
dsh webinstances on the sameDSH_HOMEcan produce it identically. The persistence layer simply has no cross-process defense.Detection (read-only scanner)
The scanner below uses the runtime's own decoder to verify every session log. It never modifies anything; run it periodically to catch corruption before a session refuses to open.
Manual repair (backup first!)
If the corruption is the overlap shape above (a batch with a stale
seq0overlapping a few rows the other writer already wrote), the history is often fully recoverable by removing the stale rows so the stream becomes contiguous again. In the capture above, deleting writer A's two stale closing rows (2400–2401) made the entire log — all 37,144 events — load again with zero content loss (A's rows were a premature duplicate close; B's continuation was the real history).Procedure used (macOS,
zstdCLI):Do not attempt this without understanding the exact rows involved — dump the decoded rows first (
seq0/seqper row around the reported line) and only remove rows that overlap. Keep the backup until the session opens correctly in the UI.Request
Please consider hardening the persistence layer against concurrent writers, any of which would have prevented this class of corruption:
DSH_HOME(e.g.DSH_HOME/.lockholding pid + port, taken at server start) with a clear error instead of a silent second writer;O_APPEND-style writes so a stale-cursor append can never overwrite a newer batch;Thanks for the great work on DSH — happy to provide the full corrupted-file capture or help test a fix.
All reactions