Skip to content

Commit 706f736

Browse files
Make releaseQuery idempotent
1 parent d0ddeab commit 706f736

File tree

1 file changed

+43
-28
lines changed

1 file changed

+43
-28
lines changed

packages/firestore/src/local/local_store.ts

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -880,34 +880,49 @@ export class LocalStore {
880880
*/
881881
// PORTING NOTE: `keepPersistedQueryData` is multi-tab only.
882882
releaseQuery(query: Query, keepPersistedQueryData: boolean): Promise<void> {
883-
const mode = keepPersistedQueryData ? 'readwrite' : 'readwrite-primary';
884-
return this.persistence.runTransaction('Release query', mode, txn => {
885-
const targetId = this.targetIdByQuery.get(query);
886-
assert(
887-
targetId !== undefined,
888-
'Tried to release nonexistent query: ' + query
889-
);
890-
const queryData = this.queryDataByTarget.get(targetId!)!;
891-
892-
// References for documents sent via Watch are automatically removed
893-
// when we delete a query's target data from the reference delegate.
894-
// Since this does not remove references for locally mutated documents,
895-
// we have to remove the target associations for these documents
896-
// manually.
897-
const removed = this.localViewReferences.removeReferencesForId(targetId!);
898-
this.queryDataByTarget = this.queryDataByTarget.remove(targetId!);
899-
this.targetIdByQuery.delete(query);
900-
901-
if (!keepPersistedQueryData) {
902-
return PersistencePromise.forEach(removed, (key: DocumentKey) =>
903-
this.persistence.referenceDelegate.removeReference(txn, key)
904-
).next(() => {
905-
this.persistence.referenceDelegate.removeTarget(txn, queryData);
906-
});
907-
} else {
908-
return PersistencePromise.resolve();
909-
}
910-
});
883+
let targetId: number;
884+
885+
const mode = keepPersistedQueryData
886+
? 'readwrite-idempotent'
887+
: 'readwrite-primary-idempotent';
888+
return this.persistence
889+
.runTransaction('Release query', mode, txn => {
890+
const cachedTargetId = this.targetIdByQuery.get(query);
891+
assert(
892+
cachedTargetId !== undefined,
893+
'Tried to release nonexistent query: ' + query
894+
);
895+
targetId = cachedTargetId!;
896+
const queryData = this.queryDataByTarget.get(targetId)!;
897+
898+
// References for documents sent via Watch are automatically removed
899+
// when we delete a query's target data from the reference delegate.
900+
// Since this does not remove references for locally mutated documents,
901+
// we have to remove the target associations for these documents
902+
// manually.
903+
// This operation needs to be run inside the transaction since EagerGC
904+
// uses the local view references during the transaction commit.
905+
// Fortunately, the operation is safe to be re-run in case the
906+
// transaction fails since there are no side effects if the target has
907+
// already been removed.
908+
const removed = this.localViewReferences.removeReferencesForId(
909+
targetId
910+
);
911+
912+
if (!keepPersistedQueryData) {
913+
return PersistencePromise.forEach(removed, (key: DocumentKey) =>
914+
this.persistence.referenceDelegate.removeReference(txn, key)
915+
).next(() => {
916+
this.persistence.referenceDelegate.removeTarget(txn, queryData);
917+
});
918+
} else {
919+
return PersistencePromise.resolve();
920+
}
921+
})
922+
.then(() => {
923+
this.queryDataByTarget = this.queryDataByTarget.remove(targetId);
924+
this.targetIdByQuery.delete(query);
925+
});
911926
}
912927

913928
/**

0 commit comments

Comments
 (0)