perf(signals): witness coarse-read machinery — one node per record subtree - #3275
perf(signals): witness coarse-read machinery — one node per record subtree#3275ryansolid wants to merge 8 commits into
Conversation
…itness node, serve raw The coarse-row read model (create-floor): a row effect witnesses its record(s) — one value-less dk node + one link each — and reads values RAW, materializing zero leaf nodes/links/wrap caches. Vue Vapor's dep economics (value-less anchors, raw data as the only value store) with Solid's semantics. Hand-compiled dbmon fixture: mount 0.70x, remount 0.77x, unmount 0.61x, tick 0.90x vs the fine-grained row. PROTOTYPE export. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…btree bumpDeep bumps the record AND every witnessed ancestor (u-chain walk gated by a live-witness counter; unwitnessed apps pay one number check). Ancestor bumps are AUTHORITATIVE — bisected: routing them through the lane machinery armed an optimistic root's dk mid-action and wedged the family generator. Fold/write gates widen so dk-less records under witnessed ancestors still bump. Full signals suite green (1461). Co-authored-by: Cursor <cursoragent@cursor.com>
…g in keyed reconcile The R18 pruning contract descends only where subscriptions exist (ct.d) — subscribing buys liveness. A deep witness on a record or ancestor IS that subscription for the whole subtree (deep() always bought it via per-record nodes; witness+bubbling claims the same observation with ONE node), so the adoption walk threads an under-witness flag: witnessed subtrees keep key-matched proxy identity without materializing any nodes. Unwitnessed captures still detach (R18 pinned tests green; full suite 1461). Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…act amendment Four pins with the __adoptVisits probe (TEST-only): (1) never-wrapped rows skip ALWAYS, witness or not — O(wrapped), never O(store); (2) unwitnessed wrapped-but-unsubscribed rows detach (R18 stale captures, unchanged); (3) under a witness, wrapped rows adopt + keep identity with zero per-row subscriptions (the amendment, and its exact cost bound); (4) releasing the witness restores pruning. The React-like diff-only-what-is-listened-to property is now a permanent suite invariant. Co-authored-by: Cursor <cursoragent@cursor.com>
… the compiler track consumes it Co-authored-by: Cursor <cursoragent@cursor.com>
…inery Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: 09b52fb The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Marked draft deliberately: this machinery is inert without its consumer (witnessNext has zero callers; bubbling/under-witness identity only activate through deep()'s dk nodes). Holding the merge until the compiler-emitted coarse-row track lands — the PR exists now so the R18-amendment semantics review can happen decoupled from the unified-For review. The one standalone behavior change if merged early: records captured under a deep() subscription keep key-matched identity through keyed reconcile instead of detaching. |
What
The coarse-read stack from the create-floor isolation battery (O5–O7), landed as internal machinery with zero public API. Three pieces, one semantic unit:
1.
witnessNext— the coarse read (O5)Subscribe to ONE record's deep-witness node (
dk) and serve the raw backing. No child walk, no key-set subscription, no snapshot, no leaf nodes, no wrap caches. This is the coarse-row read model: a row effect witnesses its record and reads values raw — one node + one link per record instead of one per touched leaf.Not exported from
solid-jsor@solidjs/signals. The consumer is the compiler-emitted coarse-row track (the unified-For scan emission); user code never calls it. Tests import it through the internal module path.2. Deep-witness bubbling (O6)
bumpDeepnow notifies witnesses on the record AND its ancestors, so one witness covers a record subtree. Gated by a live-witness counter: apps with nodeep()/witness subscribers pay one number check per change, never a chain walk. Ancestor bumps are authoritative (never lane cargo) — bisected during the battery: bubbling into an optimistic root's armeddkmid-action created an unintended override that wedged the family's generator.3. Under-witness identity (O7) — the R18 amendment
Keyed reconcile's adoption walk threads an under-witness flag: records beneath a live deep witness keep key-matched proxy identity without materializing per-record nodes — the witness IS the subscription for its subtree. The reviewer-facing semantic surface:
deep()also createsdknodes,deep()subscribers get bubbling + subtree identity too. This is a strengthening, not a break.All three behaviors are pinned by the new
reconcile-pruning-contract.test.ts(uses the__adoptVisitsprobe to assert exact walk-entry counts across unwitnessed / witnessed / witness-released scenarios).Measured
Isolation battery (solo on clean
next, 5 canonical fixtures, correctness gates green). Hand-compiled dbmon coarse rows vs fine-grained:Size
+ createStore: 14.16 → 14.30 KB (+140 B: witnessNext + bubbling + the reconcile flag)Tests
signals 1472 / web 677 / solid 585, all green. Changesets included (patch, prerelease).
Made with Cursor