Skip to content

fix(db): publish a retired replay's rows instead of keeping stale ones - #2121

Merged
KyleAMathews merged 7 commits into
mainfrom
fix-subscription-rerequest-event
Oct 13, 2026
Merged

KyleAMathews merged 7 commits into
mainfrom
fix-subscription-rerequest-event

Conversation

@KyleAMathews

@KyleAMathews KyleAMathews commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

🎯 Changes

A subscriber no longer keeps stale rows after a truncate replay that loses its last demand.

A truncate starts a replay. The subscription reloads each subset it still requests into a private buffer. It publishes the buffer when every reload finishes. If the app releases the last requested subset while the replay is loading, nothing can finish the replay. Before this change, the subscriber then kept the rows from before the truncate, even when the source replaced or deleted them during the replay.

The law

When the last demand of a replay is released:

  • If the current attempt has not failed, the subscriber receives the rows that the replay holds, in one batch. The replay completion (pendingTruncateReplacement) resolves.
  • If the current attempt failed, the subscriber keeps the last complete snapshot, even after the failed demand is released (architecture law 9). The failure stays reported through lastError and loadSubset:error, so an app can show that the refresh failed.
  • At retirement, a failure from any site in the current attempt counts. The sites are an asynchronous rejection, a replay load that throws, an old lease's unload that throws, and a new request that throws. A new attempt clears the failure.
  • An earlier failed replay can retain rows that the source no longer holds. These rows leave the published batch. A later request updates a row that the subscriber still shows at an older value.

Implementation

  • retireEmptyReplay finishes a replay that has not failed through flushTruncateReplay, so both paths publish the same way.
  • flushTruncateReplay moves its publish step into publishReplayRows. This step drops stale retained keys and then publishes the diff.
  • TruncateReplayState has a failed flag for the current attempt. The failures map cannot keep this record: release drops a demand's entry, so that the map does not keep a reference to that demand.
  • The change to subscription.ts is +33/−6.

Evidence

  • Lifecycle publication oracle:
    • Its model publishes a retired replay's rows when the attempt has not failed. A separate everFailed field applies only at full retirement, so a released demand's failure still cannot stop a successful replacement for the remaining demands.
    • Named histories cover a value written, rewritten or deleted during the replay. They also cover a row that no demand names, a failed replay, and a later request.
    • Two histories retain a row in a failed replay, delete it from the source, and then retire a later replay. Both failed on an earlier head of this PR, where the retired replay showed the deleted row. They pass now.
  • Replay oracle:
    • Its model keeps a failed flag and compares the contents of the retirement batch with the expected batch.
    • Three named witnesses make each synchronous failure site fail, and then release the last demand. Without the failed assignment at a site, the witness for that site fails. Without the assignment on the asynchronous path, five publication-oracle histories fail.
  • The tests that required AbortError for this retirement now expect the completion to resolve. Only these tests read this outcome. The live-query builder and effects use delegated publication, which this path does not change. Releasing a demand already rejects that caller's request promise.
  • @tanstack/db: 12,084 tests pass, with no type errors.

Limits

The generated grammars rarely reach a failed retirement followed by another retired replay. The named histories own that sequence. The coverage map records this limit.

✅ Checklist

  • I have tested this code locally with pnpm test.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.

This pull request and its description were written by Isaac.

Summary by CodeRabbit

  • Bug Fixes
    • Releasing the last active request during a truncate replay now publishes row changes already applied from the source when the replay has not failed, instead of leaving stale values visible.
    • If a replay attempt fails, the last complete snapshot remains visible. Later requests reconcile displayed rows with the source, including rows that have since changed or been deleted.
    • Replay retirement now completes successfully after applied changes are published, and late transport results do not overwrite those published rows.

Isaac and others added 4 commits October 12, 2026 14:28
The publication oracle's model let no request change a row the
subscriber had already seen. After a replay retired because its last
owner was released, the source kept a newer value and the subscriber
kept showing the old one indefinitely. Production reconciles that row
on the next request, which subscription.ts documents as intended. The
model now expects the update, the law prose says why, and seed
-136205996 is a named history. The witness fails against the previous
model and against a production mutant that skips the reconciliation.

Co-authored-by: Isaac <no-reply@databricks.com>
When the last requested subset was released during a truncate replay,
the replay retired without publishing, and the subscriber kept values
the source had written, replaced or deleted during it. A later request
updated only its own row; other rows stayed stale until a write changed
them, and a delete was lost. A replay whose current attempt has not
failed now publishes the rows it holds when it retires. A failed replay
keeps the last public snapshot (law 9), now even after its failed owner
is released: releasing a demand deleted its failure, so that guard
needs a flag that outlives the release.

Both subscription oracles' models follow the new law. Named histories
cover a value written, rewritten or deleted during the replay, a row no
demand names, and a failed replay; they fail on main, and a mutant that
publishes failed replays fails the failed-replay witness. ARCHITECTURE.md
and the coverage map state the rule.

Co-authored-by: Isaac <no-reply@databricks.com>
A retired replay seeded its private rows from the public snapshot, so a row
retained by an earlier failed replay and deleted by the source stayed visible
and lost its stale marker. Share flushTruncateReplay's publish step, which
removes stale keys first, with retireEmptyReplay.

Add the two ghost-row histories to the publication oracle, and witnesses for
each synchronous failure site at retirement to the replay oracle. Compare the
retire batch's contents in the replay oracle, not only its count.

Co-authored-by: Isaac <no-reply@databricks.com>
A retired replay that has not failed now finishes through
flushTruncateReplay, so its completion resolves with the published rows
instead of rejecting with AbortError. No production consumer reads that
completion on this path; the tests that pinned the abort now expect it to
resolve.

Co-authored-by: Isaac <no-reply@databricks.com>
@changeset-bot

changeset-bot Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a4d1ef0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 24 packages
Name Type
@tanstack/db Patch
@tanstack/angular-db Patch
@tanstack/capacitor-db-sqlite-persistence-e2e-app Patch
@tanstack/db-sqlite-persistence-core Patch
@tanstack/electric-db-collection Patch
@tanstack/expo-db-sqlite-persistence-e2e-app Patch
@tanstack/offline-transactions Patch
@tanstack/powersync-db-collection Patch
@tanstack/query-db-collection Patch
@tanstack/react-db Patch
@tanstack/rxdb-db-collection Patch
@tanstack/solid-db Patch
@tanstack/svelte-db Patch
@tanstack/tauri-db-sqlite-persistence-e2e-app Patch
@tanstack/trailbase-db-collection Patch
@tanstack/vue-db Patch
@tanstack/browser-db-sqlite-persistence Patch
@tanstack/capacitor-db-sqlite-persistence Patch
@tanstack/cloudflare-durable-objects-db-sqlite-persistence Patch
@tanstack/electron-db-sqlite-persistence Patch
@tanstack/expo-db-sqlite-persistence Patch
@tanstack/node-db-sqlite-persistence Patch
@tanstack/react-native-db-sqlite-persistence Patch
@tanstack/tauri-db-sqlite-persistence Patch

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

@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4ea0f8a9-5178-4511-94d9-de2ff1aa517a
📥 Commits

Reviewing files that changed from the base of the PR and between df7eb16 and a4d1ef0.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bcc9bb4a-0c04-4e56-b36c-cf134c1e232d
📥 Commits

Reviewing files that changed from the base of the PR and between 85c0f69 and f7be94d.

📒 Files selected for processing (6)
  • docs/contributing/oracle-coverage.md
  • packages/db/src/collection/subscription.ts
  • packages/db/src/query/live/ARCHITECTURE.md
  • packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts
  • packages/db/tests/collection-subscription-replay-oracle.property.test.ts
  • packages/trailbase-db-collection/src/trailbase.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/contributing/oracle-coverage.md

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.

📝 Walkthrough

The subscription now tracks failures within each truncate replay attempt. When the final demand retires, it publishes held rows if that attempt has not failed; otherwise, it preserves the last published snapshot. Oracle histories and tests cover these outcomes and later reconciliation.

Changes: Truncate replay retirement

Layer / File(s) Summary
Replay attempt failure tracking
packages/db/src/collection/subscription.ts, packages/db/tests/collection-subscription-replay-oracle.property.test.ts
Replay attempts track synchronous and asynchronous failures. New attempts reset the failure state. Regression cases check that failed replay rows remain unpublished after final-demand retirement.
Retirement publication and reconciliation
packages/db/src/collection/subscription.ts, packages/db/tests/collection-subscription-lifecycle-publication-oracle.property.test.ts, packages/db/tests/collection-subscription-replay-oracle.property.test.ts, packages/db/tests/replay-publication-storage.test.ts, packages/db/src/query/live/ARCHITECTURE.md, docs/contributing/oracle-coverage.md, .changeset/fix-retired-replay-stale-rows.md
An unfailed replay publishes its held rows when its final demand retires, and replay completion resolves. Failed attempts preserve the last published snapshot. Oracle histories and storage tests cover publication, later reconciliation, and late settlement.

|Trailbase record parsing
packages/trailbase-db-collection/src/trailbase.ts|The load path passes parsed response records directly to write and getKey, without non-null assertions.|

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Subscriber
  participant retireEmptyReplay
  participant flushTruncateReplay
  participant publishReplayRows
  Subscriber->>retireEmptyReplay: Release final demand
  retireEmptyReplay->>flushTruncateReplay: Flush rows if the attempt has not failed
  flushTruncateReplay->>publishReplayRows: Publish private replacement rows
  flushTruncateReplay-->>Subscriber: Resolve replay completion
Loading
Merge Risk: ⚪ Minimal · up to df7eb

Merge Risk: ⚪ Minimal · up to df7eb

Successful retirement publishes the replacement snapshot, including an empty one. No blocking issue was identified; the PR is ready for normal merge checks.

Pre-merge checks · Passed 4 · Failed 1

Failed Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 5 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks (4 passed)
Check name Status Explanation
Description check Passed The description follows the repository template. It explains the change and motivation, confirms local testing, and identifies the generated changeset.
Title check Passed The title clearly and concisely describes the main change: publishing a retired replay's rows instead of retaining stale rows.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 5 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Incremental update benchmark

Comparing 383d3a3d (this PR) against b112d888 (base). Write times are per-write medians over 100 iterations (20 warmup writes). Cold hydrate is one timed first load of each query.

Overall median write time vs base: 1.05× · cold hydrate time: 1.03× (geometric mean of per-case ratios; lower is faster).

Writes: 3 regression(s), 1 improvement(s) (threshold: ±20% and >0.05ms).

Cold hydrate: 2 regression(s), 1 improvement(s) (threshold: ±50% and >5ms).

Per-case flags are noisy on shared runners. Read the geometric means first.

Writes

Query Δ median write time (geomean) Best case Worst case Flags
list: newest 50 open +4.7% -41.4% +81.1% 1 🔴
list + author +4.6% -41.6% +143.8% 1 🔴
list + comment count +4.9% -47.5% +68.0% 1 🔴 1 🟢
list + 3 recent comments -8.5% -35.8% +14.1% —
issue detail + comments +22.4% -0.5% +97.5% —

Cold hydrate

Query Δ cold hydrate time (geomean) Best case Worst case Flags
list: newest 50 open -1.3% -38.4% +43.0% —
list + author +39.2% -35.0% +690.5% 2 🔴
list + comment count +0.7% -39.6% +291.1% —
list + 3 recent comments -12.7% -51.4% +73.0% 1 🟢
issue detail + comments +0.2% -20.3% +15.3% —

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.14×, cold 1.00×
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.084ms 0.101ms +21.2% 0.194ms 0.205ms +5.3% 7.387ms 8.214ms +11.2%
list: newest 50 open 0.255ms 0.267ms +4.7% 0.421ms 0.476ms +13.0% 1.936ms 1.691ms -12.7%
list + author 0.081ms 0.094ms +16.4% 0.199ms 0.167ms -16.5% 5.034ms 6.100ms +21.2%
list + comment count 0.234ms 0.221ms -5.6% 0.385ms 0.346ms -10.1% 8.676ms 8.581ms -1.1%
list + 3 recent comments 0.086ms 0.098ms +14.1% 0.204ms 0.224ms +9.7% 8.900ms 7.675ms -13.8%
issue detail + comments 0.056ms 0.078ms +39.2% 0.145ms 0.160ms +10.4% 1.290ms 1.300ms +0.7%
100 rows/collection | source indexes: none | optimistic writes — geomean 1.13×, cold 1.02×
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.041ms 0.057ms +38.9% 0.100ms 0.118ms +18.8% 1.407ms 1.331ms -5.4%
list: newest 50 open 0.162ms 0.179ms +10.7% 0.317ms 0.351ms +11.0% 1.648ms 2.236ms +35.7%
list + author 0.087ms 0.088ms +1.4% 0.154ms 0.135ms -12.5% 2.279ms 2.203ms -3.4%
list + comment count 0.108ms 0.115ms +6.6% 0.230ms 0.237ms +3.0% 4.799ms 3.482ms -27.4%
list + 3 recent comments 0.075ms 0.048ms -35.8% 0.118ms 0.133ms +13.2% 3.917ms 4.690ms +19.7%
issue detail + comments 0.036ms 0.069ms +94.1% 0.068ms 0.132ms +93.0% 0.748ms 0.782ms +4.5%
100 rows/collection | source indexes: manual | synced writes — geomean 0.91×, cold 0.93×
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.057ms 0.034ms -41.4% 0.081ms 0.056ms -29.9% 2.049ms 1.731ms -15.5%
list: newest 50 open 0.155ms 0.198ms +27.9% 0.366ms 0.303ms -17.1% 1.311ms 1.066ms -18.7%
list + author 0.066ms 0.041ms -37.6% 0.088ms 0.087ms -1.1% 2.290ms 1.488ms -35.0%
list + comment count 0.089ms 0.092ms +3.4% 0.163ms 0.185ms +13.6% 2.485ms 2.166ms -12.8%
list + 3 recent comments 0.055ms 0.058ms +6.9% 0.082ms 0.081ms -1.2% 3.478ms 6.016ms +73.0%
issue detail + comments 0.043ms 0.047ms +10.2% 0.084ms 0.098ms +16.0% 0.407ms 0.389ms -4.6%
100 rows/collection | source indexes: manual | optimistic writes — geomean 1.09×, cold 0.83×
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.030ms 0.055ms +81.1% 0.070ms 0.074ms +4.8% 0.671ms 0.668ms -0.4%
list: newest 50 open 0.103ms 0.104ms +1.0% 0.231ms 0.195ms -15.8% 0.659ms 0.753ms +14.2%
list + author 0.042ms 0.043ms +3.7% 0.088ms 0.102ms +16.4% 2.952ms 2.571ms -12.9%
list + comment count 0.082ms 0.073ms -11.7% 0.152ms 0.161ms +6.1% 3.163ms 1.911ms -39.6%
list + 3 recent comments 0.042ms 0.041ms -1.7% 0.099ms 0.089ms -9.7% 5.801ms 3.942ms -32.1%
issue detail + comments 0.036ms 0.036ms -0.4% 0.085ms 0.063ms -25.0% 0.464ms 0.370ms -20.3%
1,000 rows/collection | source indexes: none | synced writes — geomean 1.28×, cold 0.87×, 3 change(s)
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.046ms 0.029ms -37.2% 0.059ms 0.036ms -38.6% 8.244ms 5.774ms -30.0%
list: newest 50 open 0.479ms 0.551ms +15.1% 0.736ms 1.502ms +104.1% 4.360ms 3.958ms -9.2%
list + author 0.039ms 0.067ms +70.8% 0.081ms 0.105ms +30.0% 13.002ms 21.030ms +61.7% 🔴
list + comment count 0.137ms 0.231ms +68.0% 0.230ms 0.302ms +31.1% 🔴 22.827ms 20.300ms -11.1%
list + 3 recent comments 0.049ms 0.052ms +5.1% 0.062ms 0.067ms +8.5% 23.695ms 11.505ms -51.4% 🟢
issue detail + comments 0.042ms 0.084ms +97.5% 0.057ms 0.103ms +80.6% 0.962ms 0.938ms -2.6%
1,000 rows/collection | source indexes: none | optimistic writes — geomean 0.87×, cold 0.98×
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.051ms 0.033ms -35.6% 0.069ms 0.084ms +21.9% 4.113ms 5.115ms +24.4%
list: newest 50 open 0.540ms 0.557ms +3.0% 1.109ms 0.888ms -19.9% 5.108ms 3.985ms -22.0%
list + author 0.069ms 0.053ms -23.2% 0.088ms 0.086ms -1.3% 14.168ms 13.589ms -4.1%
list + comment count 0.071ms 0.085ms +18.7% 0.144ms 0.167ms +15.8% 17.669ms 21.373ms +21.0%
list + 3 recent comments 0.064ms 0.042ms -34.8% 0.082ms 0.065ms -20.6% 16.733ms 12.013ms -28.2%
issue detail + comments 0.031ms 0.034ms +7.2% 0.040ms 0.044ms +11.2% 0.831ms 0.891ms +7.3%
1,000 rows/collection | source indexes: manual | synced writes — geomean 1.16×, cold 1.12×, 1 change(s)
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.025ms 0.025ms +0.6% 0.030ms 0.032ms +5.4% 0.658ms 0.712ms +8.2%
list: newest 50 open 0.107ms 0.161ms +51.2% 0.202ms 0.209ms +3.4% 🔴 0.900ms 0.901ms +0.2%
list + author 0.034ms 0.035ms +2.6% 0.074ms 0.059ms -20.7% 1.219ms 4.306ms +253.3%
list + comment count 0.082ms 0.108ms +32.1% 0.172ms 0.206ms +19.8% 2.082ms 1.974ms -5.2%
list + 3 recent comments 0.049ms 0.052ms +7.5% 0.104ms 0.097ms -6.8% 6.431ms 3.397ms -47.2%
issue detail + comments 0.038ms 0.042ms +8.2% 0.080ms 0.084ms +5.2% 0.362ms 0.368ms +1.5%
1,000 rows/collection | source indexes: manual | optimistic writes — geomean 1.01×, cold 1.30×
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.027ms 0.027ms +2.2% 0.031ms 0.031ms -0.7% 0.594ms 0.570ms -4.1%
list: newest 50 open 0.095ms 0.108ms +13.0% 0.211ms 0.216ms +2.4% 0.673ms 0.655ms -2.7%
list + author 0.036ms 0.039ms +6.5% 0.074ms 0.077ms +3.0% 1.126ms 1.265ms +12.3%
list + comment count 0.070ms 0.081ms +15.7% 0.149ms 0.157ms +5.2% 1.706ms 6.672ms +291.1%
list + 3 recent comments 0.065ms 0.043ms -33.2% 0.078ms 0.096ms +23.1% 2.522ms 2.610ms +3.5%
issue detail + comments 0.033ms 0.037ms +12.8% 0.048ms 0.080ms +65.1% 0.351ms 0.405ms +15.3%
10,000 rows/collection | source indexes: none | synced writes — geomean 0.89×, cold 0.97×
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.029ms 0.029ms -0.8% 0.041ms 0.033ms -19.5% 35.489ms 36.696ms +3.4%
list: newest 50 open 4.482ms 4.674ms +4.3% 5.040ms 5.124ms +1.7% 36.090ms 38.316ms +6.2%
list + author 0.063ms 0.037ms -41.6% 0.079ms 0.045ms -42.8% 115.136ms 117.725ms +2.2%
list + comment count 0.076ms 0.081ms +6.2% 0.089ms 0.103ms +15.4% 161.302ms 148.290ms -8.1%
list + 3 recent comments 0.065ms 0.051ms -22.5% 0.111ms 0.102ms -7.4% 129.659ms 118.076ms -8.9%
issue detail + comments 0.035ms 0.036ms +1.3% 0.043ms 0.042ms -1.2% 3.968ms 3.489ms -12.1%
10,000 rows/collection | source indexes: none | optimistic writes — geomean 1.00×, cold 1.03×
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.030ms 0.030ms +0.8% 0.046ms 0.059ms +28.8% 34.956ms 32.166ms -8.0%
list: newest 50 open 5.223ms 5.460ms +4.5% 5.762ms 6.113ms +6.1% 40.556ms 53.883ms +32.9%
list + author 0.043ms 0.042ms -1.3% 0.073ms 0.060ms -17.7% 110.206ms 122.940ms +11.6%
list + comment count 0.097ms 0.093ms -3.9% 0.190ms 0.143ms -24.8% 176.202ms 160.389ms -9.0%
list + 3 recent comments 0.038ms 0.038ms +0.5% 0.065ms 0.054ms -17.3% 111.200ms 108.112ms -2.8%
issue detail + comments 0.033ms 0.033ms -0.5% 0.055ms 0.064ms +17.9% 3.924ms 3.878ms -1.2%
10,000 rows/collection | source indexes: manual | synced writes — geomean 1.12×, cold 1.53×, 1 change(s)
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.025ms 0.024ms -1.9% 0.035ms 0.026ms -23.9% 1.206ms 1.231ms +2.1%
list: newest 50 open 0.367ms 0.393ms +7.2% 0.603ms 0.645ms +7.0% 1.449ms 1.455ms +0.4%
list + author 0.033ms 0.037ms +11.8% 0.067ms 0.076ms +14.1% 1.809ms 14.304ms +690.5% 🔴
list + comment count 0.077ms 0.091ms +18.3% 0.097ms 0.107ms +10.4% 3.434ms 4.448ms +29.5%
list + 3 recent comments 0.050ms 0.052ms +5.4% 0.092ms 0.089ms -2.9% 3.042ms 3.463ms +13.9%
issue detail + comments 0.037ms 0.050ms +33.8% 0.046ms 0.156ms +239.9% 0.578ms 0.628ms +8.7%
10,000 rows/collection | source indexes: manual | optimistic writes — geomean 1.11×, cold 0.96×, 2 change(s)
Query Base median PR median Δ median Base p95 PR p95 Δ p95 Base cold PR cold Δ cold
list: newest 50 open 0.026ms 0.031ms +17.4% 0.049ms 0.069ms +39.3% 0.951ms 1.359ms +43.0%
list: newest 50 open 0.377ms 0.376ms -0.2% 0.430ms 0.476ms +10.8% 1.692ms 1.043ms -38.4%
list + author 0.036ms 0.088ms +143.8% 0.053ms 0.140ms +163.9% 🔴 1.992ms 2.873ms +44.2%
list + comment count 0.188ms 0.099ms -47.5% 0.253ms 0.135ms -46.5% 🟢 3.483ms 2.329ms -33.1%
list + 3 recent comments 0.039ms 0.043ms +10.8% 0.052ms 0.058ms +11.4% 3.266ms 2.746ms -15.9%
issue detail + comments 0.035ms 0.039ms +9.6% 0.046ms 0.061ms +33.3% 0.536ms 0.594ms +10.9%

Runner: node v24.8.0, linux 6.17.0-1022-azure, AMD EPYC 9V45 96-Core Processor. Timings on shared CI runners are noisy; treat small deltas as indicative only.

@pkg-pr-new

pkg-pr-new Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@2121

@tanstack/browser-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/browser-db-sqlite-persistence@2121

@tanstack/capacitor-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/capacitor-db-sqlite-persistence@2121

@tanstack/cloudflare-durable-objects-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/cloudflare-durable-objects-db-sqlite-persistence@2121

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@2121

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@2121

@tanstack/db-sqlite-persistence-core

npm i https://pkg.pr.new/@tanstack/db-sqlite-persistence-core@2121

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@2121

@tanstack/electron-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/electron-db-sqlite-persistence@2121

@tanstack/expo-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/expo-db-sqlite-persistence@2121

@tanstack/node-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/node-db-sqlite-persistence@2121

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@2121

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@2121

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@2121

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@2121

@tanstack/react-native-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/react-native-db-sqlite-persistence@2121

@tanstack/react-router-with-db

npm i https://pkg.pr.new/@tanstack/react-router-with-db@2121

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@2121

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@2121

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@2121

@tanstack/tauri-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/tauri-db-sqlite-persistence@2121

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@2121

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@2121

commit: a4d1ef0

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Size Change: +149 B (+0.07%)

Total Size: 215 kB

📦 View Changed
Filename Size Change
packages/db/dist/esm/collection/subscription.js 8.85 kB +149 B (+1.71%)
ℹ️ View Unchanged
Filename Size
packages/db/dist/esm/client.js 4.24 kB
packages/db/dist/esm/collection-options-brand.js 219 B
packages/db/dist/esm/collection-options.js 258 B
packages/db/dist/esm/collection/change-events.js 1.74 kB
packages/db/dist/esm/collection/changes.js 2.99 kB
packages/db/dist/esm/collection/cleanup-queue.js 901 B
packages/db/dist/esm/collection/config-errors.js 547 B
packages/db/dist/esm/collection/events.js 481 B
packages/db/dist/esm/collection/index.js 5.24 kB
packages/db/dist/esm/collection/indexes.js 2.3 kB
packages/db/dist/esm/collection/lifecycle.js 2.94 kB
packages/db/dist/esm/collection/mutations.js 2.99 kB
packages/db/dist/esm/collection/state.js 7.13 kB
packages/db/dist/esm/collection/sync.js 5.32 kB
packages/db/dist/esm/collection/validate-config.js 1.17 kB
packages/db/dist/esm/deferred.js 207 B
packages/db/dist/esm/error-message.js 642 B
packages/db/dist/esm/errors.js 6.49 kB
packages/db/dist/esm/event-emitter.js 1.06 kB
packages/db/dist/esm/index.js 4.49 kB
packages/db/dist/esm/indexed-db-wrapper.js 1.91 kB
packages/db/dist/esm/indexed-db.js 3.7 kB
packages/db/dist/esm/indexes/auto-index.js 929 B
packages/db/dist/esm/indexes/base-index.js 1.52 kB
packages/db/dist/esm/indexes/basic-index.js 2.02 kB
packages/db/dist/esm/indexes/btree-index.js 2.34 kB
packages/db/dist/esm/indexes/index-registry.js 916 B
packages/db/dist/esm/indexes/reverse-index.js 1 kB
packages/db/dist/esm/live-query-adapter.js 338 B
packages/db/dist/esm/live-query-observer.js 4.81 kB
packages/db/dist/esm/live-query-options.js 1.4 kB
packages/db/dist/esm/live-query-window-controller.js 4.27 kB
packages/db/dist/esm/local-only.js 1.08 kB
packages/db/dist/esm/local-storage.js 3.93 kB
packages/db/dist/esm/optimistic-action.js 359 B
packages/db/dist/esm/paced-mutations.js 1.11 kB
packages/db/dist/esm/persisted-readiness.js 195 B
packages/db/dist/esm/proxy.js 3.51 kB
packages/db/dist/esm/query/builder/clone-query.js 849 B
packages/db/dist/esm/query/builder/functions.js 1.54 kB
packages/db/dist/esm/query/builder/index.js 7.49 kB
packages/db/dist/esm/query/builder/query-ir.js 312 B
packages/db/dist/esm/query/builder/ref-proxy-identity.js 292 B
packages/db/dist/esm/query/builder/ref-proxy.js 1.67 kB
packages/db/dist/esm/query/builder/wrapper-identity.js 221 B
packages/db/dist/esm/query/compiler/evaluators.js 2.51 kB
packages/db/dist/esm/query/compiler/expressions.js 603 B
packages/db/dist/esm/query/compiler/group-by.js 4.48 kB
packages/db/dist/esm/query/compiler/index.js 10.1 kB
packages/db/dist/esm/query/compiler/joins.js 3.42 kB
packages/db/dist/esm/query/compiler/lazy-targets.js 1.11 kB
packages/db/dist/esm/query/compiler/order-by.js 2.09 kB
packages/db/dist/esm/query/compiler/parent-routes.js 319 B
packages/db/dist/esm/query/compiler/query-equivalence.js 568 B
packages/db/dist/esm/query/compiler/route-metadata.js 1.24 kB
packages/db/dist/esm/query/compiler/select.js 1.5 kB
packages/db/dist/esm/query/effect.js 5.28 kB
packages/db/dist/esm/query/equality-conjunct.js 486 B
packages/db/dist/esm/query/equality-value-identity.js 735 B
packages/db/dist/esm/query/expression-helpers.js 1.61 kB
packages/db/dist/esm/query/ir-stable-identity.js 4.2 kB
packages/db/dist/esm/query/ir.js 2.2 kB
packages/db/dist/esm/query/join-conditions.js 257 B
packages/db/dist/esm/query/live-query-collection.js 391 B
packages/db/dist/esm/query/live/bucket-facade-adapter.js 3.17 kB
packages/db/dist/esm/query/live/collection-config-builder.js 6.75 kB
packages/db/dist/esm/query/live/collection-registry.js 264 B
packages/db/dist/esm/query/live/collection-subscriber.js 2.2 kB
packages/db/dist/esm/query/live/graph-scheduler.js 305 B
packages/db/dist/esm/query/live/internal.js 145 B
packages/db/dist/esm/query/live/materialized-pipeline.js 2.39 kB
packages/db/dist/esm/query/live/ordered-source-loader.js 4.49 kB
packages/db/dist/esm/query/live/subset-demand-controller.js 1.73 kB
packages/db/dist/esm/query/live/utils.js 1.35 kB
packages/db/dist/esm/query/optimizer.js 2.95 kB
packages/db/dist/esm/query/pooled-live-query.js 4.13 kB
packages/db/dist/esm/query/query-once.js 359 B
packages/db/dist/esm/query/runtime-reference-identity.js 630 B
packages/db/dist/esm/query/subset-dedupe.js 493 B
packages/db/dist/esm/scheduler.js 1.23 kB
packages/db/dist/esm/SortedMap.js 1.6 kB
packages/db/dist/esm/strategies/commit-completion.js 156 B
packages/db/dist/esm/strategies/debounceStrategy.js 658 B
packages/db/dist/esm/strategies/queueStrategy.js 523 B
packages/db/dist/esm/strategies/serial-pacer.js 486 B
packages/db/dist/esm/strategies/throttleStrategy.js 738 B
packages/db/dist/esm/sync-persistence.js 626 B
packages/db/dist/esm/sync-receipt.js 211 B
packages/db/dist/esm/transaction-commit-work.js 351 B
packages/db/dist/esm/transactions.js 4.62 kB
packages/db/dist/esm/utils.js 1.57 kB
packages/db/dist/esm/utils/array-utils.js 270 B
packages/db/dist/esm/utils/browser-polyfills.js 304 B
packages/db/dist/esm/utils/btree.js 3.02 kB
packages/db/dist/esm/utils/callbacks.js 174 B
packages/db/dist/esm/utils/comparison.js 1.7 kB
packages/db/dist/esm/utils/cursor.js 753 B
packages/db/dist/esm/utils/error.js 262 B
packages/db/dist/esm/utils/get-or-create.js 155 B
packages/db/dist/esm/utils/index-optimization.js 2.43 kB
packages/db/dist/esm/utils/source-record.js 140 B
packages/db/dist/esm/utils/type-guards.js 230 B
packages/db/dist/esm/utils/uuid.js 530 B
packages/db/dist/esm/virtual-props.js 413 B
packages/react-db/dist/esm/DbProvider.js 317 B
packages/react-db/dist/esm/development.js 190 B
packages/react-db/dist/esm/HydrationBoundary.js 263 B
packages/react-db/dist/esm/index.js 330 B
packages/react-db/dist/esm/live-query-internals.js 282 B
packages/react-db/dist/esm/source-id-bindings.js 746 B
packages/react-db/dist/esm/useLiveInfiniteQuery.js 2.15 kB
packages/react-db/dist/esm/useLiveQuery.js 3.34 kB
packages/react-db/dist/esm/useLiveQueryEffect.js 504 B
packages/react-db/dist/esm/useLiveSuspenseQuery.js 1.33 kB
packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::db-and-react-package-size

Isaac and others added 3 commits October 12, 2026 23:05
@KyleAMathews
KyleAMathews merged commit fade2e2 into main Oct 13, 2026
26 checks passed
@KyleAMathews
KyleAMathews deleted the fix-subscription-rerequest-event branch October 13, 2026 13:36
@github-actions github-actions Bot mentioned this pull request Oct 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR has been released!

Thank you for your contribution!

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