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

60 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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](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](04-bug-lossy-change-detection.md)/[12](12-opt-hot-path-full-model-work.md)
rework lands — that change touches the same conversion layer, and doing the slot
refinement then amortizes the rewrite.