pcbjam/docs/features/ysync-review/14-opt-item-granularity-bandwidth.md
Viktor Vaczi 80135a99ac
docs(ysync-review): repro suite results + empirical findings (doc 16); plan 15 executed
New doc 16: the full repro-suite map (per-bug unit/e2e paths with verified
failure sites), the phase-C probe outcome, and four findings only the RUNNING
system revealed:
- F1: bug 03's sending half emits NOTHING — a child-only delete commit never
  triggers a flush at all (worse than the doc's predicted bare removed-wire);
  the GetWidth-assert tracer evidence and the fix implication.
- F2: Firefox cannot host two kicad_editor tabs in one context (per-process
  wasm budget) — bug-01 two-tab repros are Chromium-only.
- F3: headless emit WORKS on both pcbnew and eeschema — the legacy two-tab
  skip rationale and items-bridge localEdit omissions are stale.
- F4: drift-detect is strictly ITEM-silent on the green path (no writer-
  formatting false positives).

Cross-updates: 00 index + verdict note; 01/04/05/06/07 Verification sections
gain their repro paths; 02 upgraded to runtime-CONFIRMED; 03 gains the F1
empirical correction; 11 (no v2 e2e coverage) CLOSED with a point-by-point
status update — only the legacy retirement remains.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPfrVhfYgPPgtawjSssZfn
2026-07-06 08:56:11 +02:00

3.3 KiB
Raw Blame History

Optimization 14 — Item-level body granularity: whole-item payloads per nudge, LWW drops concurrent property edits

Severity: known/documented v1 tradeoff — recorded here so its costs are visible when prioritizing Status: open (deliberate design decision; revisit trigger below)

Where

  • web/pcbjam-shared/src/kicad-y.ts:23-26 — the documented v1 choice: an item's body is ONE plain-JSON value → item-level merge; "deep Y types per slot (field-level merge inside one item) are the later refinement"
  • upsertYItem (kicad-y.ts:60-72) — any body change rewrites the whole body value

Costs as shipped

  1. Bandwidth / update-log growth ∝ item size, not edit size. Nudging a footprint 1 mm re-writes its full flattened subtree into the Y.Doc: the update ships the whole body JSON, and the sync server persists it in the room's update log until compaction. For a 100-pad footprint that's kilobytes per nudge. (The flatten already helps a lot — pads/fields/pins are separate items, so a child edit only re-writes the child + the lifted parent body — but the parent body itself embeds {item} refs plus all non-item slots, and pcbnew's liftBlob re-emits the parent for every child change.)
  2. Last-writer-wins at item granularity. Two peers concurrently editing two different properties of the same item (one moves a text, the other edits its string) resolve by Yjs LWW on body — one peer's edit is silently discarded. Convergent, but lossy in exactly the case CRDTs are chosen for. The flatten means this only bites within one item (concurrent pad edits on the same footprint are fine — different items), which is why it's been acceptable so far.
  3. Body comparison is JSON.stringify equality (upsertYItem, sameKicadItem) — fine at current sizes; becomes part of the hot-path cost at scale (12-opt-hot-path-full-model-work.md).

The refinement path (when justified)

The kicad-doc.ts header already sketches it: map each body/v slot list to a Y.Array of slot Y.Maps instead of one JSON value.

  • Concurrent different-slot edits merge instead of LWW-dropping.
  • Updates ship only changed slots.
  • The zod schema remains the post-merge structural check (this is why slot lists were designed as uniform ordered arrays — the shape is already CRDT-ready).

Costs to respect:

  • ordered-list CRDT semantics introduce interleaving anomalies for concurrent inserts at the same position (slot order is file order — usually stable, so low risk);
  • the conversion layer (itemsWireToDelta / deltaToItemsWire / yToItem) must become slot-diff-aware — a meaningful rewrite of kicad-y.ts's write path;
  • per-slot Y overhead (item count × slot count Y structs) raises baseline doc size.

Revisit trigger

Not worth doing speculatively. Revisit when either:

  • server-side room storage / bandwidth per session becomes a measured cost, or
  • concurrent same-item edits become a real reported UX complaint (e.g. two people routing in the same area fighting over one track's endpoints), or
  • the 04/12 rework lands — that change touches the same conversion layer, and doing the slot refinement then amortizes the rewrite.