Skip to content

Failing repro: loadSubset resolves & live query marks ready while synced rows are parked behind a persisting transaction#1662

Draft
TomasGonzalez wants to merge 1 commit into
TanStack:mainfrom
TomasGonzalez:cor-4466-loadsubset-persisting-deferral-repro
Draft

Failing repro: loadSubset resolves & live query marks ready while synced rows are parked behind a persisting transaction#1662
TomasGonzalez wants to merge 1 commit into
TanStack:mainfrom
TomasGonzalez:cor-4466-loadsubset-persisting-deferral-repro

Conversation

@TomasGonzalez

Copy link
Copy Markdown

What this is

A failing test (no fix) reproducing an on-demand loadSubset readiness bug in @tanstack/db. Opened as draft so the repro runs in CI — happy to turn it into a fix PR once maintainers weigh in on the approach.

  • Adds packages/db/tests/query/load-subset-persisting-deferral.test.ts
  • Control test passes; the persisting-transaction case fails (received []). Verified failing on current main (expected [] to deeply equal [ 'r1', 'r2' ]).

The bug

In on-demand sync mode, await loadSubset(...), collection.status === 'ready', and toArrayWhenReady() all resolve before the rows loadSubset fetched are readable, whenever any user transaction on the collection is in state persisting (an in-flight optimistic mutation).

commit() is fire-and-forget: commitPendingTransactions() in packages/db/src/collection/state.ts skips applying committed synced transactions while hasPersistingTransaction (guard: !hasPersistingTransaction || hasTruncateSync || hasImmediateSync), leaving the rows parked in pendingSyncedTransactions. Nothing downstream is signalled, so loadSubset resolves, the live query calls markReady(), and the read returns []. The rows become readable only when the mutation settles.

The test's loadSubset begins/writes/commits synchronously then resolves — exactly what @tanstack/electric-db-collection does inside its subscribe handler on a subset-end control message before requestSnapshot resolves. @electric-sql/client is not involved and not at fault (it delivers to subscribers before resolving since 1.5.20).

Why it matters

Downstream (offline-first apps using @tanstack/offline-transactions) this surfaces as silently empty/partial reads whenever a background write is in flight — e.g. duplicating a template's child rows into a new record returns nothing. Settle-window/timer workarounds cannot recover the rows, because a parked commit emits no change events until the transaction settles.

Possible fixes (happy to implement)

  • loadSubset shouldn't resolve until its synced transaction(s) are applied (e.g. commit() returns a promise / a whenApplied() signal). This also fixes the DeduplicatedLoadSubset coverage-poisoning, since tracking runs on resolution.
  • Or commit subset-snapshot data with begin({ immediate: true }) (already bypasses the guard; used today for resume-metadata resets). Authoritative server rows have nothing to reconcile against.
  • Or make live-query readiness account for committed-but-parked synced transactions.

Related

…y while synced rows are parked behind a persisting transaction

On-demand collections: `toArrayWhenReady()` resolves with `[]` for rows that
`loadSubset` fetched and committed, whenever any user transaction on the
collection is in state `persisting`. `commitPendingTransactions()`
(collection/state.ts) parks the committed synced transaction while
`hasPersistingTransaction`, but `loadSubset` still resolves and the live query
still calls `markReady()`, so the read reports ready over a stale/empty snapshot.

Control test (no mutation in flight) passes; the persisting case fails
(receives `[]`). Verified failing on current `main`. Related: TanStack#1017, TanStack#1657.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d02df8b9-0c7a-4a41-b5ee-c2f477ad9b3c

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@KyleAMathews

Copy link
Copy Markdown
Collaborator

Thanks for the excellent reproduction — this is exactly the kind of report that's easy to act on.

We verified it against main: the control case passes and the persisting case fails precisely as you described (expected [] to deeply equal ['r1','r2']), and the mechanism checks out — the commitPendingTransactions guard silently parks committed synced transactions with no downstream signal, commit() returns void, and DeduplicatedLoadSubset records the predicate as covered on resolution, so retries can't recover. We also confirmed your begin({ immediate: true }) direction makes the repro pass, though with the caveat that an immediate commit flushes all parked sync transactions.

This is now tracked in the loadSubset/pagination RFC (#1657) as evidence item E13 with a dedicated work item (§3.8): loadSubset resolution must mean the rows are actually applied — your test is the red test for it. A maintainer will be picking up the RFC work soon, and we'd rather land this as a proper fix at the contract level within that effort than as a one-off patch — that's the whole point of the cluster analysis. We'll keep this PR open for its repro until then.

KyleAMathews added a commit that referenced this pull request Jul 10, 2026
PR #1615 independently implements the local-ready fix (RFC PR 6 bullet 1),
resolving Open Decision 1 as local-first if approved. Cross-reference
#1662 (ready-while-rows-parked repro in core), merged #1626 (#1618), and
merged #1644.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants