fix(electric): bound refresh wait for on-demand subsets#1575
fix(electric): bound refresh wait for on-demand subsets#1575KyleAMathews wants to merge 2 commits into
Conversation
📝 WalkthroughWalkthroughBounds waiting for Electric stream refreshes to 250ms before on-demand subset loading; if the refresh doesn't finish, the code proceeds to request a snapshot. Timer cleanup is ensured via a finally block. Changeset documents the patch. ChangesBounded Electric Stream Refresh
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
More templates
@tanstack/angular-db
@tanstack/browser-db-sqlite-persistence
@tanstack/capacitor-db-sqlite-persistence
@tanstack/cloudflare-durable-objects-db-sqlite-persistence
@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/query-db-collection
@tanstack/react-db
@tanstack/react-native-db-sqlite-persistence
@tanstack/rxdb-db-collection
@tanstack/solid-db
@tanstack/svelte-db
@tanstack/tauri-db-sqlite-persistence
@tanstack/trailbase-db-collection
@tanstack/vue-db
commit: |
|
Size Change: 0 B Total Size: 122 kB ℹ️ View Unchanged
|
|
Size Change: 0 B Total Size: 4.24 kB ℹ️ View Unchanged
|
Summary
On-demand Electric live queries no longer wait indefinitely for
forceDisconnectAndRefresh()before requesting subset snapshots. This prevents React Native/Expo-style native fetch implementations that don't promptly abort long polls from keeping live queries inloadinguntil the Electric live poll naturally times out.Root Cause
For on-demand sync,
@tanstack/electric-db-collectionasks Electric for a subset snapshot when a live query needs data. If the underlyingShapeStreamis already up-to-date, the collection first calls:That is intended to abort an in-flight live long-poll and force a fresh stream round-trip before
requestSnapshot(). In browsers this normally returns quickly becausefetchaborts the long-poll promptly.Some native fetch implementations, especially React Native/Expo, may not reject/abort long-poll requests promptly. In that case,
forceDisconnectAndRefresh()can remain pending until the long-poll times out. Because the live query tracks the subset load promise, the query can keep reportingloadingeven after rows are present locally.Approach
Bound the refresh wait with a small timeout:
If the refresh finishes quickly, behavior is unchanged. If it does not finish within
250ms, the collection proceeds torequestSnapshot()instead of waiting for the long-poll to finish naturally.Key Invariants
requestSnapshot()still runs after the bounded refresh attempt.handleSnapshotError()path.Non-goals
fetchClient.Trade-offs
A fully awaited refresh is slightly stricter, but it can turn native fetch abort quirks into user-visible multi-second or multi-minute loading states. A bounded wait preserves the intended fast path while making the on-demand path resilient when abort is slow or ignored.
The timeout is intentionally short (
250ms) because the refresh is only a pre-snapshot optimization. If it cannot complete quickly, making progress withrequestSnapshot()is better than holding the live query in loading until the live poll naturally returns.Verification
pnpm --filter @tanstack/electric-db-collection build pnpm --filter @tanstack/electric-db-collection testBoth passed.
Note: in a fresh worktree, running the filtered test before building produced type-resolution errors for e2e suite imports of
@tanstack/electric-db-collection. Building the package first fixed that, and the package tests passed.Files changed
packages/electric-db-collection/src/electric.tsFORCE_DISCONNECT_AND_REFRESH_TIMEOUT_MS.stream.forceDisconnectAndRefresh()in a boundedPromise.race()before on-demand subset snapshot requests..changeset/tame-rn-live-poll-refresh.md@tanstack/electric-db-collection.