From dbbb2ebc7eb8e59302be153d409cb1652982dedd Mon Sep 17 00:00:00 2001 From: Ido Ziv Date: Mon, 31 Aug 2026 15:08:37 +0300 Subject: [PATCH 1/2] db: release synced keys after delete --- .changeset/release-deleted-sync-keys.md | 5 +++++ packages/db/src/collection/state.ts | 1 + packages/db/tests/collection.test.ts | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .changeset/release-deleted-sync-keys.md diff --git a/.changeset/release-deleted-sync-keys.md b/.changeset/release-deleted-sync-keys.md new file mode 100644 index 0000000000..01c5f368b8 --- /dev/null +++ b/.changeset/release-deleted-sync-keys.md @@ -0,0 +1,5 @@ +--- +'@tanstack/db': patch +--- + +Release synced key tracking when rows are deleted. diff --git a/packages/db/src/collection/state.ts b/packages/db/src/collection/state.ts index 3783422013..338479750b 100644 --- a/packages/db/src/collection/state.ts +++ b/packages/db/src/collection/state.ts @@ -1061,6 +1061,7 @@ export class CollectionStateManager< break } case `delete`: + this.syncedKeys.delete(key) this.syncedData.delete(key) this.syncedMetadata.delete(key) // Clean up origin and pending tracking for deleted rows diff --git a/packages/db/tests/collection.test.ts b/packages/db/tests/collection.test.ts index 3ff8ede815..5338daf1fa 100644 --- a/packages/db/tests/collection.test.ts +++ b/packages/db/tests/collection.test.ts @@ -1656,7 +1656,7 @@ describe(`Collection`, () => { }) }) - it(`should delete row metadata when sync deletes the row`, async () => { + it(`should release row tracking when sync deletes the row`, async () => { let testSyncFunctions: any = null const collection = createCollection<{ id: number; value: string }>({ @@ -1681,6 +1681,7 @@ describe(`Collection`, () => { await collection.stateWhenReady() expect(collection._state.syncedMetadata.get(1)).toEqual({ source: `sync` }) + expect(collection._state.syncedKeys.has(1)).toBe(true) const { begin, write, commit } = testSyncFunctions begin() @@ -1691,6 +1692,7 @@ describe(`Collection`, () => { commit() expect(collection._state.syncedMetadata.has(1)).toBe(false) + expect(collection._state.syncedKeys.has(1)).toBe(false) }) it(`should not retain a synced metadata entry for inserts without metadata`, async () => { From fd4be9647f6c19a21a7dda8eb72dd0ed6ad765b0 Mon Sep 17 00:00:00 2001 From: Ido Ziv Date: Mon, 31 Aug 2026 15:36:44 +0300 Subject: [PATCH 2/2] db: isolate synced key deletion regression test --- packages/db/tests/collection.test.ts | 38 +++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/db/tests/collection.test.ts b/packages/db/tests/collection.test.ts index 5338daf1fa..511f6b161a 100644 --- a/packages/db/tests/collection.test.ts +++ b/packages/db/tests/collection.test.ts @@ -1656,7 +1656,41 @@ describe(`Collection`, () => { }) }) - it(`should release row tracking when sync deletes the row`, async () => { + it(`should remove deleted keys from synced key tracking`, async () => { + const collection = createCollection( + mockSyncCollectionOptionsNoInitialState<{ + id: number + value: string + }>({ + id: `sync-key-delete-test`, + getKey: (item) => item.id, + startSync: true, + }), + ) + + collection.utils.begin() + collection.utils.write({ + type: `insert`, + value: { id: 1, value: `one` }, + }) + collection.utils.write({ + type: `insert`, + value: { id: 2, value: `two` }, + }) + collection.utils.commit() + collection.utils.markReady() + + await collection.stateWhenReady() + expect(collection._state.syncedKeys).toEqual(new Set([1, 2])) + + collection.utils.begin() + collection.utils.write({ type: `delete`, key: 1 }) + collection.utils.commit() + + expect(collection._state.syncedKeys).toEqual(new Set([2])) + }) + + it(`should delete row metadata when sync deletes the row`, async () => { let testSyncFunctions: any = null const collection = createCollection<{ id: number; value: string }>({ @@ -1681,7 +1715,6 @@ describe(`Collection`, () => { await collection.stateWhenReady() expect(collection._state.syncedMetadata.get(1)).toEqual({ source: `sync` }) - expect(collection._state.syncedKeys.has(1)).toBe(true) const { begin, write, commit } = testSyncFunctions begin() @@ -1692,7 +1725,6 @@ describe(`Collection`, () => { commit() expect(collection._state.syncedMetadata.has(1)).toBe(false) - expect(collection._state.syncedKeys.has(1)).toBe(false) }) it(`should not retain a synced metadata entry for inserts without metadata`, async () => {