From 8710d26a5a0fcec1e712edd169bb7afbe1f895c2 Mon Sep 17 00:00:00 2001 From: Sam Willis Date: Wed, 27 Aug 2025 11:40:24 +0100 Subject: [PATCH 1/4] handle truncate in next transaction so no flash of empty collection --- packages/electric-db-collection/src/electric.ts | 5 ++--- packages/electric-db-collection/tests/electric.test.ts | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/electric-db-collection/src/electric.ts b/packages/electric-db-collection/src/electric.ts index a89331700..1e070127a 100644 --- a/packages/electric-db-collection/src/electric.ts +++ b/packages/electric-db-collection/src/electric.ts @@ -540,9 +540,8 @@ function createElectricSync>( truncate() - // Commit the truncate transaction immediately - commit() - transactionStarted = false + // Reset hasUpToDate so we continue accumulating changes until next up-to-date + hasUpToDate = false } } diff --git a/packages/electric-db-collection/tests/electric.test.ts b/packages/electric-db-collection/tests/electric.test.ts index e8167efbb..6ea0c1b1b 100644 --- a/packages/electric-db-collection/tests/electric.test.ts +++ b/packages/electric-db-collection/tests/electric.test.ts @@ -254,8 +254,10 @@ describe(`Electric Integration`, () => { }, ]) - // The collection should be cleared but remain in ready state - expect(collection.state.size).toBe(0) + // The collection should still have old data because truncate is in pending + // transaction. This is the intended behavior of the collection, you should have + // the old data until the next up-to-date message. + expect(collection.state.size).toBe(2) expect(collection.status).toBe(`ready`) // Send new data after must-refetch From 36a94d19127110ad0346b13b219a73f784ab6f76 Mon Sep 17 00:00:00 2001 From: Sam Willis Date: Wed, 27 Aug 2025 12:19:40 +0100 Subject: [PATCH 2/4] add logging of errors that have marked a collection as ready --- packages/electric-db-collection/src/electric.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/electric-db-collection/src/electric.ts b/packages/electric-db-collection/src/electric.ts index 1e070127a..4d4a65150 100644 --- a/packages/electric-db-collection/src/electric.ts +++ b/packages/electric-db-collection/src/electric.ts @@ -476,7 +476,7 @@ function createElectricSync>( return { sync: (params: Parameters[`sync`]>[0]) => { - const { begin, write, commit, markReady, truncate } = params + const { begin, write, commit, markReady, truncate, collection } = params const stream = new ShapeStream({ ...shapeOptions, signal: abortController.signal, @@ -487,6 +487,13 @@ function createElectricSync>( if (shapeOptions.onError) { return shapeOptions.onError(errorParams) + } else { + console.error( + `An error occurred while syncing collection: ${collection.id}, \n` + + `it has been marked as ready to avoid blocking apps waiting for '.preload()' to finish. \n` + + `You can provide an 'onError' handler on the shapeOptions to handle this error, and this message will not be logged.`, + errorParams + ) } return From 4d1c92897980e010d4bdb0b968ff57458b58d402 Mon Sep 17 00:00:00 2001 From: Sam Willis Date: Wed, 27 Aug 2025 12:21:27 +0100 Subject: [PATCH 3/4] changeset --- .changeset/eleven-towns-carry.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/eleven-towns-carry.md diff --git a/.changeset/eleven-towns-carry.md b/.changeset/eleven-towns-carry.md new file mode 100644 index 000000000..4333b572a --- /dev/null +++ b/.changeset/eleven-towns-carry.md @@ -0,0 +1,5 @@ +--- +"@tanstack/electric-db-collection": patch +--- + +fix the handling of an electric must-refetch message so that the truncate is handled in the same transaction as the next up-to-date, ensuring you don't get a momentary empty collection. From cb8ca2990318b164470719f49eae50096625f8ef Mon Sep 17 00:00:00 2001 From: Sam Willis Date: Wed, 27 Aug 2025 12:24:04 +0100 Subject: [PATCH 4/4] add comment on 409 --- packages/electric-db-collection/src/electric.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/electric-db-collection/src/electric.ts b/packages/electric-db-collection/src/electric.ts index 4d4a65150..6655020c4 100644 --- a/packages/electric-db-collection/src/electric.ts +++ b/packages/electric-db-collection/src/electric.ts @@ -483,6 +483,9 @@ function createElectricSync>( onError: (errorParams) => { // Just immediately mark ready if there's an error to avoid blocking // apps waiting for `.preload()` to finish. + // Note that Electric sends a 409 error on a `must-refetch` message, but the + // ShapeStream handled this and it will not reach this handler, therefor + // this markReady will not be triggers by a `must-refetch`. markReady() if (shapeOptions.onError) {