Skip to content

fix(server): extend persister quiesce fence to snapshot writes — merge, don't clobber (CWD-4) - #324

Merged
eliotlim merged 6 commits into
mainfrom
fix/cwd4-persister-fence
Aug 14, 2026
Merged

fix(server): extend persister quiesce fence to snapshot writes — merge, don't clobber (CWD-4)#324
eliotlim merged 6 commits into
mainfrom
fix/cwd4-persister-fence

Conversation

@eliotlim

Copy link
Copy Markdown
Collaborator

Problem

With OPENBOOK_SERVER_PERSIST=1, a stale client whole-snapshot PUT /api/pages/:id (pure LWW) could clobber a newer server checkpoint — the persister's quiesce/reseed freeze protocol existed but only the version-restore route used it (PVH-8). Board: CWD-4 (epic OB-76).

Solution

The persister now owns a reusable quiesce→write→finalize boundary with intent discrimination: restores keep overwrite semantics (drop + reseed + relay.forget, now correctly ordered via an afterWrite hook); client snapshot PUTs merge — the snapshot's Yjs update is applied to the retained canonical doc and checkpointed as the CRDT union, so neither the stale client's op nor the server's merged state is ever lost (review probe: durable end-state strictly better than base, which silently dropped the PUT's op). Same-page writes serialize; cross-page writes don't; freezes are refcounted so a concurrent DELETE can't strip another writer's guard; shutdown drains in-flight fences; cold pages (no live session) skip the machinery entirely. Merge checkpoints preserve per-block authorship (OB-170). Persist-off path byte-identical.
Key files: packages/server/src/collabPersist.ts, app.ts. New deps: none.

Before / After

No visual change — server persistence semantics (flag-gated; OPENBOOK_SERVER_PERSIST off in prod today).

Behavior (persist on) Before After
Stale snapshot PUT during active collab Clobbered newer server checkpoint (LWW) Quiesce-fenced; durable end-state = CRDT union; convergence test pins it
Restore during in-flight checkpoint Fenced (PVH-8) Same guarantees via shared boundary; relay.forget now inside the freeze window
PUT {"name":"x"} (no data) 200 200 (mid-review 500 fixed + tested both modes)
PUT authorship n/a Verified author survives the merge checkpoint (was wiped mid-review; fixed + tested)
Cold-page PUT Direct write Direct write (no canonical-doc manufacture, no extra checkpoint)

Test procedure

pnpm --filter @book.dev/server test -- src/serverPersist.test.ts src/collabPersist.test.ts   # 18 + 14
pnpm verify

Review verification (Quinn, two full rounds with probes): two-client durability probe base vs branch (union convergence), refcount probed four ways, serialization event-order probe, restore parity.

Operational notes

None today (flag off in prod). This closes the double-writer that blocked enabling server persistence; the real-Postgres interleaving proof (checkpoint-in-flight vs PUT) is specced for the CWD-11 harness as a follow-up test. Known accepted gap: relay view is a safe superset of durable until the next checkpoint (pre-existing T1 asymmetry, unchanged).


Verify: green (full pnpm verify). Reviews cleared: code (Quinn — round 1 BLOCKER [durability inversion] fixed and re-verified by probe; round 2 N1-N3 + nits applied, all pre-endorsed).

🤖 Generated with Claude Code

https://claude.ai/code/session_018RUicirLRuuZvjJN13N6yN

@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app.book.pub Ready Ready Preview Aug 14, 2026 2:54pm

Request Review

@eliotlim
eliotlim merged commit 4123f67 into main Aug 14, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant