Skip to content

fix(db-sqlite-persistence-core): persisted preload() hangs when upstream sync never calls markReady#1615

Open
PHILLIPS71 wants to merge 6 commits into
TanStack:mainfrom
PHILLIPS71:fix/persisted-preload-offline-hang
Open

fix(db-sqlite-persistence-core): persisted preload() hangs when upstream sync never calls markReady#1615
PHILLIPS71 wants to merge 6 commits into
TanStack:mainfrom
PHILLIPS71:fix/persisted-preload-offline-hang

Conversation

@PHILLIPS71

@PHILLIPS71 PHILLIPS71 commented Jun 25, 2026

Copy link
Copy Markdown

🎯 Changes

collection.preload() and toArrayWhenReady() hung forever when the upstream sync never called markReady(). This happens in when TanStack Query's networkMode: 'offlineFirst' pauses a query because the device is offline, paused queries never reach isSuccess or isError, so markReady() is never called, and consumers block indefinitely even though SQLite has local data available.

Fix

After ensureStarted() resolves (SQLite hydration complete), params.markReady() is now called directly in createWrappedSyncConfig, independent of the upstream sync. This is skipped in on-demand mode because no rows load at startup there, the upstream sync owns readiness in that case. If the upstream sync also calls markReady(), the second call is safely ignored by the lifecycle layer.

✅ Checklist

  • I have tested this code locally with pnpm test.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes

    • Fixed a case where preload() could hang if upstream synchronization never signaled readiness.
    • Improved persisted sync readiness so loading completes on success or startup failure, while preventing late readiness signals after cleanup (including correct behavior for on-demand mode).
  • Tests

    • Added regression coverage for preload() resolving without upstream readiness, plus additional readiness/cleanup race-condition tests to ensure readiness is signaled correctly and only once.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f81acf01-897f-4f54-990e-67a190cbfbca

📥 Commits

Reviewing files that changed from the base of the PR and between 6bb2bfd and 4486d4b.

📒 Files selected for processing (2)
  • packages/db-sqlite-persistence-core/src/persisted.ts
  • packages/db-sqlite-persistence-core/tests/persisted.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/db-sqlite-persistence-core/src/persisted.ts

📝 Walkthrough

Walkthrough

Persisted SQLite sync startup now signals readiness when upstream sync omits markReady, while preventing duplicate or late signals after cleanup. Tests cover preload completion, on-demand behavior, startup failures, readiness races, and cleanup.

Changes

Persisted preload readiness

Layer / File(s) Summary
Wrapped sync readiness
packages/db-sqlite-persistence-core/src/persisted.ts
syncMode is exposed to wrapper logic, and wrapped startup now signals readiness once after success or failure while respecting cleanup and on-demand mode.
Loopback startup and readiness regression
packages/db-sqlite-persistence-core/src/persisted.ts, packages/db-sqlite-persistence-core/tests/persisted.test.ts, .changeset/free-rivers-like.md
Loopback startup signals readiness after ensureStarted() settles; tests cover missing upstream readiness, failures, races, and cleanup, and the changeset records the patch release.

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

Suggested reviewers: kevin-dp, kyleamathews

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the core fix in db-sqlite-persistence-core and matches the changeset.
Description check ✅ Passed The description follows the template with Changes, Checklist, and Release Impact, and includes the required changeset and test notes.
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.
✨ 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.

@PHILLIPS71 PHILLIPS71 changed the title fix: persisted preload() hangs when upstream sync never calls markReady fix(db-sqlite-persistence-core): persisted preload() hangs when upstream sync never calls markReady Jun 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/db-sqlite-persistence-core/src/persisted.ts`:
- Around line 2563-2570: The loopback startup flow in ensureStarted() still
calls params.markReady() unconditionally in the finally block, which can race
with cleanup() and re-mark a cleaned-up collection ready. Update the persisted
loopback path to mirror the guarded wrapped path by checking
startupState.cleanedUp before calling params.markReady(). Keep the fix localized
around the runtime.ensureStarted().catch(...).finally(...) chain in persisted.ts
so the lifecycle transition stays valid even when startup finishes late.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b90cf813-03a8-4896-9265-35c0a866016b

📥 Commits

Reviewing files that changed from the base of the PR and between 45617c4 and e17122d.

📒 Files selected for processing (3)
  • .changeset/free-rivers-like.md
  • packages/db-sqlite-persistence-core/src/persisted.ts
  • packages/db-sqlite-persistence-core/tests/persisted.test.ts

Comment thread packages/db-sqlite-persistence-core/src/persisted.ts
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>
@pkg-pr-new

pkg-pr-new Bot commented Jul 13, 2026

Copy link
Copy Markdown
More templates

@tanstack/angular-db

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

@tanstack/browser-db-sqlite-persistence

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

@tanstack/capacitor-db-sqlite-persistence

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

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

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/db-sqlite-persistence-core

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

@tanstack/electric-db-collection

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

@tanstack/electron-db-sqlite-persistence

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

@tanstack/expo-db-sqlite-persistence

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

@tanstack/node-db-sqlite-persistence

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

@tanstack/offline-transactions

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

@tanstack/powersync-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

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

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/tauri-db-sqlite-persistence

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: 4486d4b

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