Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

Summary

Fixes critical error propagation issues where errors from Electric SQL and TanStack Query weren't being propagated to collections, causing:

  1. preload() hanging indefinitely - When sync errors occurred, apps waiting for preload() would block forever
  2. Collections marked ready with no data - Collections would show status: 'ready' even when they had failed to sync any data

Changes

Electric-db-collection (packages/electric-db-collection/src/electric.ts)

  • Implement 10-second grace period before setting error status, allowing Electric's built-in retry logic to recover from transitory network issues
  • Remove premature markReady() call that was incorrectly marking collections ready before data synced
  • Clear error timer on successful recovery

Query-db-collection (packages/query-db-collection/src/query.ts)

  • Set error status immediately after TanStack Query exhausts all retry attempts
  • No grace period needed since TanStack Query handles retries internally

preload() fix (packages/db/src/collection/sync.ts)

  • Listen for status:error events and reject the promise
  • Prevents indefinite hangs when sync fails

Collection lifecycle improvements (packages/db/src/collection/)

  • Added markError() method for sync implementations to set error state
  • Made events property public (was _events private)
  • markReady() now auto-handles error recovery by transitioning through loading state (error → loading → ready)
  • Valid state transitions updated to support recovery: error can transition to idle or loading

Test plan

  • ✅ All 66 tests passing in @tanstack/electric-db-collection
  • ✅ All 58 tests passing in @tanstack/query-db-collection
  • ✅ New tests verify:
    • Error status set after 10 seconds of continuous errors (electric)
    • preload() rejects after 10 seconds of errors
    • Error timer cleared on recovery within grace period
    • Transitory errors recover naturally without setting error status

🤖 Generated with Claude Code

Errors from Electric SQL and TanStack Query weren't being propagated to collections, causing two critical issues:

1. `preload()` would hang indefinitely when sync errors occurred, blocking apps waiting for data
2. Collections would be marked as 'ready' even when they had no synced data, leading to empty results

**Changes:**

- **electric-db-collection**: Implement 10-second grace period before marking collection as errored, allowing Electric's built-in retry logic to recover from transitory network issues. Remove premature `markReady()` call that was marking collections ready with no data.

- **query-db-collection**: Set error status immediately after TanStack Query exhausts retries (no grace period needed since Query handles retries internally).

- **preload()**: Listen for `status:error` events and reject the promise, preventing indefinite hangs when sync fails.

- **Collection lifecycle**: Add `markError()` method and make `events` public. Auto-handle error recovery in `markReady()` by transitioning through loading state (error → loading → ready).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Oct 13, 2025

⚠️ No Changeset found

Latest commit: 4b743b9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 13, 2025

More templates

@tanstack/angular-db

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/electric-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: 4b743b9

@github-actions
Copy link
Contributor

github-actions bot commented Oct 13, 2025

Size Change: +122 B (+0.15%)

Total Size: 81.7 kB

Filename Size Change
./packages/db/dist/esm/collection/index.js 3.31 kB +1 B (+0.03%)
./packages/db/dist/esm/collection/lifecycle.js 1.86 kB +58 B (+3.22%)
./packages/db/dist/esm/collection/sync.js 1.75 kB +63 B (+3.74%)
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 963 B
./packages/db/dist/esm/collection/changes.js 1.01 kB
./packages/db/dist/esm/collection/events.js 660 B
./packages/db/dist/esm/collection/indexes.js 1.16 kB
./packages/db/dist/esm/collection/mutations.js 2.52 kB
./packages/db/dist/esm/collection/state.js 3.79 kB
./packages/db/dist/esm/collection/subscription.js 1.83 kB
./packages/db/dist/esm/deferred.js 230 B
./packages/db/dist/esm/errors.js 3.5 kB
./packages/db/dist/esm/index.js 1.63 kB
./packages/db/dist/esm/indexes/auto-index.js 794 B
./packages/db/dist/esm/indexes/base-index.js 835 B
./packages/db/dist/esm/indexes/btree-index.js 2 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.21 kB
./packages/db/dist/esm/indexes/reverse-index.js 577 B
./packages/db/dist/esm/local-only.js 967 B
./packages/db/dist/esm/local-storage.js 2.33 kB
./packages/db/dist/esm/optimistic-action.js 294 B
./packages/db/dist/esm/proxy.js 3.86 kB
./packages/db/dist/esm/query/builder/functions.js 615 B
./packages/db/dist/esm/query/builder/index.js 4.04 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 938 B
./packages/db/dist/esm/query/compiler/evaluators.js 1.55 kB
./packages/db/dist/esm/query/compiler/expressions.js 760 B
./packages/db/dist/esm/query/compiler/group-by.js 2.04 kB
./packages/db/dist/esm/query/compiler/index.js 2.19 kB
./packages/db/dist/esm/query/compiler/joins.js 2.63 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.26 kB
./packages/db/dist/esm/query/compiler/select.js 1.28 kB
./packages/db/dist/esm/query/ir.js 785 B
./packages/db/dist/esm/query/live-query-collection.js 404 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.3 kB
./packages/db/dist/esm/query/live/collection-registry.js 233 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.86 kB
./packages/db/dist/esm/query/optimizer.js 3.26 kB
./packages/db/dist/esm/scheduler.js 1.29 kB
./packages/db/dist/esm/SortedMap.js 1.24 kB
./packages/db/dist/esm/transactions.js 3.05 kB
./packages/db/dist/esm/utils.js 1.01 kB
./packages/db/dist/esm/utils/browser-polyfills.js 365 B
./packages/db/dist/esm/utils/btree.js 6.01 kB
./packages/db/dist/esm/utils/comparison.js 754 B
./packages/db/dist/esm/utils/index-optimization.js 1.73 kB

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

github-actions bot commented Oct 13, 2025

Size Change: 0 B

Total Size: 1.46 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 152 B
./packages/react-db/dist/esm/useLiveQuery.js 1.31 kB

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


// Managers
private _events: CollectionEventsManager
public events: CollectionEventsManager
Copy link
Collaborator

Choose a reason for hiding this comment

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

the manager classes are intended to be a private api, but have to be public so that they can see each other, and we can introspect them from tests. This should change it to public, but maintain the underscore on the name.

The "public" face of the events api is a set of methods on the Collection class itself that forward to this manager.

Suggested change
public events: CollectionEventsManager
public _events: CollectionEventsManager

Comment on lines +147 to +150
// If recovering from error, transition to loading first
if (this.status === `error`) {
this.setStatus(`loading`)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't like this, markReady should only move you to a ready state not loading.

We do not have a way in live queries to handle a ready => loading

How much handling of errors and moving between states to we expect the sync implementation to do? It seems to me there are two options for a collection:

  1. handle error recovery with no braking of the synced state, as they do with a truncate, and so a markReady to go from error -> ready makes sense.
  2. catastrophic "unrecoverable" error and the sync needs to be restarted. In that case a restartSync() method that the sync handler can call that:
  • calls .cleanup(), doing a gc and moves to cleaned-up
  • then calls .startSync(), which moves to loading and finally ready once the sync marks it as such.

Option 2 has some nuances arround subscriptions we would need to handle. Existing subscription would also need to be restarted, but we had not implemented that yet, it would be the truncate message from issue #634 proposing a refactor of the reconciliation process.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Whats this?

KyleAMathews pushed a commit that referenced this pull request Oct 20, 2025
…cerns

Comprehensive analysis of GitHub issue #672 and samwillis' feedback on PR #671:

- Document current error handling state across collection types
- Analyze proposed first-class error tracking in CollectionLifecycleManager
- Examine samwillis' concerns about error state transitions
- Compare Graceful Recovery vs Catastrophic Restart models
- Identify design questions around state transition validity
- Propose solution path supporting both recovery patterns
- Highlight backwards compatibility and testing considerations

Key findings:
- Query collections use closure-based error tracking
- Electric collections lack error tracking entirely
- Current transitions don't allow error → ready or error → loading
- Live queries can't handle ready → loading transitions
- Two distinct error recovery patterns needed for different scenarios

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <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