Skip to content

Commit 9c1baf1

Browse files
Make releaseQuery idempotent (#2266)
1 parent d0ba462 commit 9c1baf1

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

920935
/**

0 commit comments

Comments
 (0)