From 30ab8e7f111b4b50fbc261d1009174713832c58f Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Fri, 10 Jul 2026 08:53:11 -0600 Subject: [PATCH 1/2] refactor(query-db-collection): extract query ownership helpers --- packages/query-db-collection/src/query.ts | 101 ++++++++++++---------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/packages/query-db-collection/src/query.ts b/packages/query-db-collection/src/query.ts index ea303b8dd..55470b8c4 100644 --- a/packages/query-db-collection/src/query.ts +++ b/packages/query-db-collection/src/query.ts @@ -697,28 +697,58 @@ export function queryCollectionOptions( // 3. Decrements refcount and GCs rows where count reaches 0 const queryRefCounts = new Map() - // Helper function to add a row to the internal state - const addRow = (rowKey: string | number, hashedQueryKey: string) => { - const rowToQueriesSet = rowToQueries.get(rowKey) || new Set() - rowToQueriesSet.add(hashedQueryKey) - rowToQueries.set(rowKey, rowToQueriesSet) - - const queryToRowsSet = queryToRows.get(hashedQueryKey) || new Set() - queryToRowsSet.add(rowKey) - queryToRows.set(hashedQueryKey, queryToRowsSet) + const addRowOwner = (rowKey: string | number, hashedQueryKey: string) => { + const owners = rowToQueries.get(rowKey) || new Set() + owners.add(hashedQueryKey) + rowToQueries.set(rowKey, owners) + + const ownedRows = + queryToRows.get(hashedQueryKey) || new Set() + ownedRows.add(rowKey) + queryToRows.set(hashedQueryKey, ownedRows) } - // Helper function to remove a row from the internal state - const removeRow = (rowKey: string | number, hashedQuerKey: string) => { - const rowToQueriesSet = rowToQueries.get(rowKey) || new Set() - rowToQueriesSet.delete(hashedQuerKey) - rowToQueries.set(rowKey, rowToQueriesSet) + const addRowOwners = (rowKey: string | number, owners: Set) => { + rowToQueries.set(rowKey, new Set(owners)) + owners.forEach((owner) => { + const ownedRows = queryToRows.get(owner) || new Set() + ownedRows.add(rowKey) + queryToRows.set(owner, ownedRows) + }) + } + + const removeRowOwner = (rowKey: string | number, hashedQueryKey: string) => { + const owners = rowToQueries.get(rowKey) || new Set() + owners.delete(hashedQueryKey) + rowToQueries.set(rowKey, owners) + + const ownedRows = + queryToRows.get(hashedQueryKey) || new Set() + ownedRows.delete(rowKey) + queryToRows.set(hashedQueryKey, ownedRows) + + return owners.size === 0 + } + + const removeQueryOwnership = (hashedQueryKey: string) => { + const nextOwnersByRow = new Map>() + + const rowKeys = + queryToRows.get(hashedQueryKey) ?? new Set() + + rowKeys.forEach((rowKey) => { + const owners = rowToQueries.get(rowKey) + + if (!owners) { + return + } - const queryToRowsSet = queryToRows.get(hashedQuerKey) || new Set() - queryToRowsSet.delete(rowKey) - queryToRows.set(hashedQuerKey, queryToRowsSet) + const nextOwners = new Set(owners) + nextOwners.delete(hashedQueryKey) + nextOwnersByRow.set(rowKey, nextOwners) + }) - return rowToQueriesSet.size === 0 + return nextOwnersByRow } const internalSync: SyncConfig[`sync`] = (params) => { @@ -853,12 +883,7 @@ export function queryCollectionOptions( continue } - rowToQueries.set(rowKey, new Set(owners)) - owners.forEach((owner) => { - const queryToRowsSet = queryToRows.get(owner) || new Set() - queryToRowsSet.add(rowKey) - queryToRows.set(owner, queryToRowsSet) - }) + addRowOwners(rowKey, owners) if (owners.has(hashedQueryKey)) { ownedRows.add(rowKey) @@ -944,12 +969,7 @@ export function queryCollectionOptions( return } - rowToQueries.set(row.key, new Set(ownerSet)) - ownerSet.forEach((owner) => { - const queryToRowsSet = queryToRows.get(owner) || new Set() - queryToRowsSet.add(row.key) - queryToRows.set(owner, queryToRowsSet) - }) + addRowOwners(row.key, ownerSet) if (ownerSet.has(hashedQueryKey)) { baseline.set(row.key, { @@ -975,7 +995,7 @@ export function queryCollectionOptions( baseline.forEach(({ value: oldItem, owners }, rowKey) => { owners.delete(hashedQueryKey) setPersistedOwners(rowKey, owners) - const needToRemove = removeRow(rowKey, hashedQueryKey) + const needToRemove = removeRowOwner(rowKey, hashedQueryKey) if (needToRemove) { rowsToDelete.push(oldItem) } @@ -1321,7 +1341,7 @@ export function queryCollectionOptions( const owners = getPersistedOwners(key) owners.delete(hashedQueryKey) setPersistedOwners(key, owners) - const needToRemove = removeRow(key, hashedQueryKey) + const needToRemove = removeRowOwner(key, hashedQueryKey) if (needToRemove) { write({ type: `delete`, value: oldItem }) } @@ -1336,7 +1356,7 @@ export function queryCollectionOptions( owners.add(hashedQueryKey) setPersistedOwners(key, owners) } - addRow(key, hashedQueryKey) + addRowOwner(key, hashedQueryKey) if (!currentSyncedItems.has(key)) { write({ type: `insert`, value: newItem }) } @@ -1506,21 +1526,10 @@ export function queryCollectionOptions( cancelPersistedRetentionExpiry(hashedQueryKey) retainedQueriesPendingRevalidation.delete(hashedQueryKey) - const rowKeys = queryToRows.get(hashedQueryKey) ?? new Set() - const nextOwnersByRow = new Map>() + const nextOwnersByRow = removeQueryOwnership(hashedQueryKey) const rowsToDelete: Array = [] - rowKeys.forEach((rowKey) => { - const queries = rowToQueries.get(rowKey) - - if (!queries) { - return - } - - const nextOwners = new Set(queries) - nextOwners.delete(hashedQueryKey) - nextOwnersByRow.set(rowKey, nextOwners) - + nextOwnersByRow.forEach((nextOwners, rowKey) => { if (nextOwners.size === 0 && collection.has(rowKey)) { rowsToDelete.push(collection.get(rowKey)) } From 298c3924ff6e0f77f0bc3c7cba4a7bd274d7a57b Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Fri, 10 Jul 2026 08:53:45 -0600 Subject: [PATCH 2/2] chore: add query ownership helpers changeset --- .changeset/extract-query-ownership-helpers.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/extract-query-ownership-helpers.md diff --git a/.changeset/extract-query-ownership-helpers.md b/.changeset/extract-query-ownership-helpers.md new file mode 100644 index 000000000..df07298f0 --- /dev/null +++ b/.changeset/extract-query-ownership-helpers.md @@ -0,0 +1,5 @@ +--- +'@tanstack/query-db-collection': patch +--- + +Extract internal query row ownership helpers to make lifecycle cleanup paths easier to reason about while preserving existing behavior.