Repository navigation
fix(db): preserve observer row sharing and freshness - #2346
Conversation
Cache row materialization separately from full observer snapshots. Keep lazy state Maps and data arrays stable across status/health changes while preserving value-only updates, layout changes, and retained point-in-time reads. Law: metadata-only snapshots retain their row containers (maintainer decision 2026-11-01; LiveQuerySnapshot contract). Reach: 52 fixed and 52 random histories cross both observer modes, attached and detached reads, empty/nonempty rows, read order, readiness, health, and writes. RED: unchanged production fails metadata reference equality; seed 1101, path 0:0:0:0, in live-query-observer.row-container-sharing. GREEN: 100 observer tests, 89 existing Angular tests (one TODO), focused types, ESLint, oracle lint, mangle validation, package build, and docs snippets pass on Node 24.8.0. The patch Changeset releases this independently of Angular fixes. Mutant: same-cardinality reuse fails a same-key value write; seed 1101, path 0:0:1:2. The final implementation is restored. Limits: the new lane uses synchronous direct Collections with 0..3 string keys; existing ordered/findOne/hydration suites cover neighboring laws. No new host scheduling or external-provider guarantee.
Exercise subscribe/unsubscribe transitions, require genuine health-only reach, and register the sharing lane with guarded replay. Reuse captured entries for layout comparison and materialize the shared Map directly. Law: same-row-revision metadata changes share containers; values and retained snapshots remain correct. Authority: LiveQuerySnapshot and maintainer decision. Reach: 24 examples plus 40 generated histories per fixed/random campaign; nonempty attach/detach/reattach reached in both modes. RED: original implementation fails metadata identity, seed 1101 path 0:0:0:0. GREEN: 112 observer tests, guarded replay, TypeScript, lint, mangle, and minified API checks pass. Mutant: unconditional entry replacement fails guarded replay at seed 1101, path 2:3:1:6:4:3; cardinality-only reuse and false health reach also fail. Limits: synchronous direct Collection path; no new host/provider or reset sharing claim. Full evidence is in the observer row-container review record. Production observer is 75 minified bytes smaller than the original merge-base; DB package delta is -34 gzip and -111 brotli bytes.
🦋 Changeset detectedLatest commit: acdfb6f The changes in this PR will be included in the next version bump. This PR includes changesets to release 25 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 |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
Incremental update benchmarkComparing Overall median write time vs base: 0.96× · cold hydrate time: 1.03× (geometric mean of per-case ratios; lower is faster). Writes: 0 regression(s), 3 improvement(s) (threshold: ±20% and >0.05ms). Cold hydrate: 3 regression(s), 1 improvement(s) (threshold: ±50% and >5ms). Per-case flags are noisy on shared runners. Read the geometric means first. Writes
Cold hydrate
Each row aggregates the 14.4 scale/index/write-mode configurations of that query; per-configuration tables below. 100 rows/collection | source indexes: none | synced writes — geomean 1.05×, cold 1.10×
100 rows/collection | source indexes: none | optimistic writes — geomean 0.98×, cold 1.07×
100 rows/collection | source indexes: manual | synced writes — geomean 1.06×, cold 0.99×
100 rows/collection | source indexes: manual | optimistic writes — geomean 1.04×, cold 1.09×
1,000 rows/collection | source indexes: none | synced writes — geomean 0.75×, cold 0.99×, 1 change(s)
1,000 rows/collection | source indexes: none | optimistic writes — geomean 1.05×, cold 0.92×
1,000 rows/collection | source indexes: manual | synced writes — geomean 0.98×, cold 0.96×
1,000 rows/collection | source indexes: manual | optimistic writes — geomean 0.91×, cold 1.24×, 1 change(s)
10,000 rows/collection | source indexes: none | synced writes — geomean 1.04×, cold 1.07×
10,000 rows/collection | source indexes: none | optimistic writes — geomean 0.76×, cold 1.15×, 1 change(s)
10,000 rows/collection | source indexes: manual | synced writes — geomean 1.04×, cold 1.07×, 2 change(s)
10,000 rows/collection | source indexes: manual | optimistic writes — geomean 0.95×, cold 0.80×
Runner: node v24.8.0, linux 7.0.0-1017-azure, AMD EPYC 9V45 96-Core Processor. Timings on shared CI runners are noisy; treat small deltas as indicative only. |
|
Size Change: +42 B (+0.02%) Total Size: 239 kB 📦 View Changed
ℹ️ View Unchanged
|
More templates
@tanstack/angular-db
@tanstack/browser-db-sqlite-persistence
@tanstack/capacitor-db-sqlite-persistence
@tanstack/cloudflare-durable-objects-db-sqlite-persistence
@tanstack/convex-db-collection
@tanstack/db
@tanstack/db-ivm
@tanstack/db-sqlite-persistence-core
@tanstack/electric-db-collection
@tanstack/electron-db-sqlite-persistence
@tanstack/expo-db-sqlite-persistence
@tanstack/node-db-sqlite-persistence
@tanstack/offline-transactions
@tanstack/powersync-db-collection
@tanstack/preact-db
@tanstack/query-db-collection
@tanstack/react-db
@tanstack/react-native-db-sqlite-persistence
@tanstack/react-router-with-db
@tanstack/rxdb-db-collection
@tanstack/solid-db
@tanstack/svelte-db
@tanstack/tauri-db-sqlite-persistence
@tanstack/trailbase-db-collection
@tanstack/vue-db
commit: |
Law: LiveQuerySnapshot and the 2026-11-01 maintainer decision require unchanged rows to retain containers, while values and ordered keys stay fresh. No-op actions also preserve full-snapshot identity. Reach: 17 sharing and 16 real-query capture states have fixed witnesses; fixed/random campaigns use 24+40 and 16+40 histories respectively. All measured counts are in the versioned evidence record. RED: Parent e02e09c fails metadata keeps data after refresh, subscribe, report. Seed 1101, path 2:4:5:6:8:5. New receiving paths passed before edits. GREEN: 118 observer tests and 41 replay tests pass, plus TypeScript, ESLint, oracle lint, minified build/API and mangle checks. Observer -99 minified bytes; DB -40 gzip and -97 brotli bytes against the original merge-base. Mutant: Original wrapper comparison fails guarded replay. Ignoring layout clocks fails ordered rows at seed 2346, path 0:2:0:1:1:0. Snapshot cloning survives the old lane and fails the added no-op assertion. Limits: Core direct/query snapshot cuts, bounded rows/actions, no arbitrary concurrency, framework/provider timing, cleanup sharing, caller-mutation isolation or infinite-page-array identity promise. No budget decreased.
Keep committed row revisions monotonic and fence discarded layout clocks. Publish nested record prototype changes under the maintainer decision. Remove unsupported clockless compatibility and redundant observer capture work. Law: committed reads remain fresh, retained snapshots keep capture-time rows, and metadata-only updates share containers. Record prototype changes publish under the maintainer decision of 2026-11-03. Draft equality remains separate. Reach: capture histories reach all 27 declared states, with nested discarded value/layout writes, both modes, ordered/findOne, hydration and reattachment. Record-shape histories reach all 12 states through source and query receivers. RED: unchanged 71bb4c6 fails 8 intended assertions in the extended five-owner gate; clock seed2346/path0 and shape seed234602/path0:1:0:2:2 fail publicly. GREEN: 1423 tests in15 suites pass; latest utility reach rerun83/83 passes. Focused types, lint, minified API/build, mangle validation and guarded replay pass. Mutant: original row rollback, layout-fence omission and old record equality all fail the intended comparisons. All production files restored afterward. Limits: bounded core sync histories and queued-query reads; no host/provider timing, exotic built-in subclasses, cleanup sharing or infinite page identity. Full evidence and nine-finding dispositions are in docs/contributing/oracle-reviews/2026-11-01-observer-row-containers.md. Shipped minified bytes are net -339 versus the original merge base.
Resolve the record-equality conflict while retaining upstream signed-zero, comparison-mode and Effect publication policies. Law: retain fresh committed captures, metadata sharing and record prototypes, under the established contracts and maintainer decision of 2026-11-03. Reach: all sharing, capture and shape states reached on the merged tree. RED: row rollback, layout-fence omission and old prototype equality each fail the merged oracle at the intended public comparison. GREEN: 1868 tests in16 suites, focused types, lint, minified API and mangle pass. Mutant: all three repeated against merged main and rejected; guarded replay passes after restoring production. Limits: bounded core/query cuts; combined Angular tree and full local workspace suite unverified. Exact pushed-head CI is separate. Production remains net -339 minified bytes against current main1443dc92e. Full evidence: docs/contributing/oracle-reviews/2026-11-01-observer-row-containers.md.
Law: unordered structural Sets require a bijection; AGENTS.md excludes combinatorial comparison work. Supported pooled observers publish the same rows, status and deltas as their live-query Collection. Prototype publication follows the maintainer decision of 2026-11-03. Reach: utils.42 runs 100 fixed + 100 random histories; all 12 declared states reach 200. Pooled wholesale/granular/detached receivers each reach 160. RED: extended utility owner on f66d684 fails scalar read 2049 > 2048 (seed 20260911, path 0:0:0). Extended pooled owner fails subscription before repair (seed 44502001, path 0:0:0:0:0:0). GREEN: final focused source gate 190 tests; prior broader gate 786 tests; built-dist 357 tests; real Preact conformance 136 tests; pooled GC 21 tests. Types, ESLint, changed-file oracle lint, minified API and mangle checks pass. Mutant: factorial matcher and precheck-only matcher fail work assertions; candidate reuse fails bag equality. Silent pooled attach fallback fails the granular mirror (seed 44502001, path 1:0:0:0:3:2:2). All mutants restored. Limits: bounded scalar Set work, corresponding parent cycles, core pooled histories and real Preact receiving witness. No global nested-value cost or latest combined Angular tree claim. Production is -285 minified bytes against main1443dc92e. See the versioned row-container review report for the audits.
Restore generic record equality while retaining prototype-sensitive Collection publication. Reuse Set cycle bookkeeping, prefer free candidates, and enforce observer revision clocks before resource acquisition. Simplify capture callers. Law: generic and row equality retain their distinct approved domains; paired equal Sets need bounded work; invalid Collection clocks reject before mutation. Reach: small Set 13 states x 200 histories; paired Set 8 states x 20 histories. RED: extended pre-fix utility gate fails paired work (4097 > 4096) and generic record equality; ten constructor invariant witnesses fail before that repair. GREEN: seven focused suites pass 381 tests; types, ESLint, changed-file lint, minified API, production error erasure, and mangle checks pass. Mutant: no paired fast path, strict generic prototypes, greedy-only assignment, and original factorial matching all fail intended observations in direct replay. Limits: arbitrary Set reorder/nested cost and graph topology are unbounded; #2349 draft prototype loss is explicitly outside this PR. #2334 stays open. Production: -262 minified bytes vs exact origin/main 1443dc9.
🎯 Changes
Status and health updates replace the observer's row containers even when no rows change. Consumers that compare those containers by reference can repeat row work after each metadata update.
Capture each row revision once and share its data array and lazy state Map across metadata snapshots. Metadata changes still produce a fresh full snapshot with current status and health. Value writes replace the row containers, and retained snapshots keep their captured rows. Membership and ordering changes remain visible through
layoutRevision. Actions that change no public fact retain the full snapshot identity.The cache trusts Collection's row and layout clocks. Equal-content writes can create fresh virtual wrappers without changing those clocks, so snapshots keep their existing capture. This PR also fixes three freshness hazards at that boundary:
Supported inputs are Collections and pooled views from this DB instance. Reject absent or non-finite revision clocks before client binding, row reads, or sync activation. Pooled views use their ordinary change stream for layout updates, so the separate layout channel stays optional. Remove the unsupported clockless compatibility branch and check clocks before enumerating unchanged rows. Apply client hydration captures once through the publication queue. The cache keeps references to the live capture and last materialized visible capture, which can differ between publication and a read.
Review also found factorial Set comparison and a quadratic common-case regression in its first fix. First compare corresponding members, which establishes equality in linear work for paired equal inputs. Otherwise, build candidates and find a one-to-one assignment, preferring free candidates before reassignment. The utility oracle checks large equal inputs, multiplicity deficits, and a case that requires reassignment. Generic
deepEqualsretains its established keyed-class/plain/null/cross-realm record policy. Only Collection row equality gains the approved prototype distinction.This preserves @benjavicente's original commit from #2328 and adds the oracle, freshness, and size improvements. The source PR disables maintainer edits, so this PR contains the follow-up changes. Production changes affect the shared observer, Collection invalidation, and equality utility in
@tanstack/db. Framework adapter sources stay unchanged. The Angular adapter's separate row-state projection belongs to #2327. The latest combined tree with that PR remains unverified.The size comparison uses current main
1443dc92e, identical production defines, and identical compression:All eight app scenarios shrink by 49–83 gzip bytes. Brotli changes range from −161 to +55 bytes. The DB export and group-by scenario grow under brotli compression. A deterministic unchanged granular-attachment probe reduces row enumerations from two to one. The remaining enumeration is Collection subscription bootstrap. A real hydration handoff applies the observer cache once instead of twice. The prototype check adds no traversal and uses prototypes the comparison already reads.
🔬 Oracle Coverage
The primary snapshot owner is
packages/db/tests/live-query-observer-snapshot-oracle.property.test.ts. Its sharing, capture-path, and record-shape properties check independent row/rank and shape/number models.e02e09c70fails equal-refresh attachment sharing. Unchanged71bb4c659fails eight intended assertions in the extended five-owner gate. These include discarded value/layout captures and prototype replacements through both receivers. Clock seed2346, path0, retains version 2 instead of 3. Shape seed234602, path0:1:0:2:2, retains plain shape after a class replacement.From
packages/db, run the snapshot owner with:pnpm exec vitest run tests/live-query-observer-snapshot-oracle.property.test.ts --coverage.enabled=false --maxWorkers=2The versioned evidence record contains the laws, reach counts, replay inputs, mutants, measurements, and the external and four-agent review audits. The first review records five fixed findings, three refutations, and two accepted design choices. The second records seven fixed findings, one refutation, and one accepted design choice. The supplement records both CodeRabbit reviews, the Set work fix, and CI corrections. The third external review records five fixes, one accepted readonly-sharing design, and the explicitly separate draft bug #2349. Editing a nested sibling can temporarily strip an untouched class/null-prototype leaf. The maintainer identified that bug and kept its fix outside this PR. No known counterexample remains in the metadata-sharing and discarded-publication scope. The separate cleanup/restart issue #2334 remains open. Its shared-model trace also fails after readiness in both modes on
162eebf39. The coverage map records its owner and next witness. This PR does not establish general observer lifecycle correctness. The coverage map records the owners and their limits.✅ Checklist
pnpm test.The full workspace suite did not run locally. The focused gates above pass. CI for
22da3b06ecompleted with 25 successful checks and one skipped CodeRabbit check. CI for779466c62found the missing generated error page and an Angular test mock without a layout clock. The fixes address both mechanical omissions. CI for5911d5e00found five source-type errors in the new sparse-array driver and a separate ordered-loader oracle failure. The driver correction passes both sparse campaigns, the full DB TypeScript source check, ESLint, and changed-file lint. The original sparse-array mutant still fails all receiving paths. The loader failure reproduces identically on exact main1443dc92e; its seed, shrink path, full trace, owner, and next experiment are preserved in the coverage map. Its repair location remains unresolved. The independently replayed trace is tracked in #2296. The maintainer approved merging this scoped observer fix with that investigation tracked separately. No test, budget, or seed was changed to bypass it. CI for corrected headacdfb6f14completed successfully: 25 successful checks and one skipped CodeRabbit check. Production remains atba336f4b2. CodeRabbit is paused and its latest covered production commit remains162eebf39; it has not reviewed the later semantic changes. This green random campaign does not close the recorded loader counterexample, the separate #2334 cleanup/restart defect, or the unverified combined #2327 integration. General readiness remains unproven. The separate Electric sampling reach issue remains tracked in #2347.🚀 Release Impact
observer-row-container-sharing.md, preserved from perf(db): preserve row references across metadata snapshots #2328).Summary by CodeRabbit