Skip to content

perf(core): bulk-initialize Mirror snapshots with verified container identities - #95

Closed
zxch3n wants to merge 4 commits into
fix/ignore-root-eventsfrom
perf/bulk-init-deep-value
Closed

zxch3n wants to merge 4 commits into
fix/ignore-root-eventsfrom
perf/bulk-init-deep-value

Conversation

@zxch3n

@zxch3n zxch3n commented Sep 3, 2026 •

Copy link
Copy Markdown
Member

Problem and behavior

Mirror initialization previously traversed document containers through many individual WASM calls. This PR builds state, $cid markers, schema registrations, and inference options from one doc.getDeepValueWithID() snapshot and a JS walk. Trees and unsupported shapes retain the handle-based fallback; versions without the deep-value API retain the legacy snapshot path.

Container identity is verified against a cached parent shallow read before a child wrapper is interpreted. Embedded user objects such as { cid: "cid:root-other:Map", value: { message: "kept" }, extra: 123 } remain intact rather than being unwrapped. This also handles empty containers with omitted wrappers in loro-crdt 1.13.3. Old mergeable-reference binary markers use a targeted get() check to distinguish containers from user bytes.

The deep-value CID parser supports Debug-format and bare IDs. Unknown-root normalization, Ignore fields, decoding, and incremental state behavior are retained.

Validation

  • Build, 525 tests, lint (zero errors), and workspace typecheck passed on the standalone branch.
  • New regressions cover embedded wrapper-shaped values in maps, lists, movable lists, and unknown roots, plus snapshot consistency. The minimal counterexample fails on the original PR head and passes after the correction.
  • Existing bulk/legacy parity fuzz and mergeable-container tests pass.

Performance limitation

The correctness fix requires additional parent shallow reads; this is not a one-WASM-call initialization. The benchmark now reports these reads explicitly.

Same-process Node 22 / locked loro-crdt 1.13.3 comparison, 171 entries × 30 items (~67k containers), 3 warmups and 10 samples, explicit GC:

Path p50 Instrumented reads
Corrected bulk 385.586 ms 1 deep read + 15,733 parent shallow reads; zero List.get/Map.get/Text.toJSON
Legacy control 254.110 ms 15,561 List.get + 56,772 Map.get + 51,300 Text.toJSON

The corrected bulk path is slower in this measurement. The original speedup / projected 2× claim is not an acceptance result for the corrected code; performance needs further work before this PR should be merged as an optimization. Read counts above are instrumented methods, not every WASM crossing.

Published loro-crdt 1.16.0 follow-up

Updated core/react/jotai loro-crdt requirements and the lockfile to ^1.16.0 / 1.16.0. Build, 525 tests, lint (0 errors), and typecheck pass. A fresh default ~67k-container benchmark on the published package reports bulk p50 360.044 ms (1 deep read, 15,733 instrumented parent shallow reads). The older 1.13.3 comparison above remains historical; this change does not replace the standalone performance acceptance requirement. #98 now tests the published container-tree path.

…call

Mirror initialization previously walked the document container by
container (~10 wasm crossings per container: id/kind/get/keys/toJSON).
buildRootStateSnapshot now reads the whole doc with a single
doc.getDeepValueWithID() call and builds state, $cid markers,
containerRegistry, inferOptionsByContainerId and rootPathById in one JS
walk that replicates registerChildContainer/containerToMirrorState
semantics exactly. No container handles are created during init; trees
and unhandled kinds fall back to the existing handle-based logic, and
the legacy traversal is kept as a fallback for loro-crdt versions
without getDeepValueWithID.

- Defensive cid parsing: tolerates both the Debug-like
  "idx:N, id:cid:..." format and bare container ids.
- loro-crdt <1.15 drops the {value,cid} wrapper for empty containers,
  making them indistinguishable from embedded plain values; the walk
  disambiguates via the parent's cached getShallowValue() (one extra
  call per affected parent only).
- Adds a seeded parity fuzz test (bulk vs legacy: state, $cid via
  cidsEqual, registry, infer options) and a dependency-free benchmark
  (pnpm bench:bulk-init, ~67k-container session-history-shaped doc):
  wasm crossings during init drop from ~527k to 1.
@zxch3n zxch3n changed the title perf(core): bulk-initialize Mirror state with one getDeepValueWithID call perf(core): bulk-initialize Mirror snapshots with verified container identities Sep 5, 2026
@zxch3n
zxch3n changed the base branch from main to fix/ignore-root-events September 6, 2026 07:11
@zxch3n zxch3n closed this Sep 6, 2026
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