Skip to content

Commit 74621b7

Browse files
Make notifyLocalViewChanges idempotent (#3044)
1 parent a57dac5 commit 74621b7

13 files changed

+123
-128
lines changed

packages/firestore/src/core/sync_engine.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -682,8 +682,7 @@ export class SyncEngine implements RemoteSyncer {
682682
this.queriesByTarget.delete(targetId);
683683

684684
if (this.isPrimaryClient) {
685-
const limboKeys = this.limboDocumentRefs.referencesForId(targetId);
686-
this.limboDocumentRefs.removeReferencesForId(targetId);
685+
const limboKeys = this.limboDocumentRefs.removeReferencesForId(targetId);
687686
limboKeys.forEach(limboKey => {
688687
const isReferenced = this.limboDocumentRefs.containsKey(limboKey);
689688
if (!isReferenced) {

packages/firestore/src/local/indexeddb_mutation_queue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ export class IndexedDbMutationQueue implements MutationQueue {
519519
return PersistencePromise.forEach(
520520
removedDocuments,
521521
(key: DocumentKey) => {
522-
return this.referenceDelegate.removeMutationReference(
522+
return this.referenceDelegate.markPotentiallyOrphaned(
523523
transaction,
524524
key
525525
);

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ import {
7070
ReferenceDelegate
7171
} from './persistence';
7272
import { PersistencePromise } from './persistence_promise';
73-
import { ReferenceSet } from './reference_set';
7473
import { ClientId } from './shared_client_state';
7574
import { TargetData } from './target_data';
7675
import { SimpleDb, SimpleDbStore, SimpleDbTransaction } from './simple_db';
@@ -1070,8 +1069,6 @@ function clientMetadataStore(
10701069

10711070
/** Provides LRU functionality for IndexedDB persistence. */
10721071
export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
1073-
private inMemoryPins: ReferenceSet | null = null;
1074-
10751072
readonly garbageCollector: LruGarbageCollector;
10761073

10771074
constructor(private readonly db: IndexedDbPersistence, params: LruParams) {
@@ -1081,14 +1078,14 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
10811078
getSequenceNumberCount(
10821079
txn: PersistenceTransaction
10831080
): PersistencePromise<number> {
1084-
const docCountPromise = this.orphanedDocmentCount(txn);
1081+
const docCountPromise = this.orphanedDocumentCount(txn);
10851082
const targetCountPromise = this.db.getTargetCache().getTargetCount(txn);
10861083
return targetCountPromise.next(targetCount =>
10871084
docCountPromise.next(docCount => targetCount + docCount)
10881085
);
10891086
}
10901087

1091-
private orphanedDocmentCount(
1088+
private orphanedDocumentCount(
10921089
txn: PersistenceTransaction
10931090
): PersistencePromise<number> {
10941091
let orphanedCount = 0;
@@ -1113,19 +1110,17 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
11131110
);
11141111
}
11151112

1116-
setInMemoryPins(inMemoryPins: ReferenceSet): void {
1117-
this.inMemoryPins = inMemoryPins;
1118-
}
1119-
11201113
addReference(
11211114
txn: PersistenceTransaction,
1115+
targetId: TargetId,
11221116
key: DocumentKey
11231117
): PersistencePromise<void> {
11241118
return writeSentinelKey(txn, key);
11251119
}
11261120

11271121
removeReference(
11281122
txn: PersistenceTransaction,
1123+
targetId: TargetId,
11291124
key: DocumentKey
11301125
): PersistencePromise<void> {
11311126
return writeSentinelKey(txn, key);
@@ -1141,7 +1136,7 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
11411136
.removeTargets(txn, upperBound, activeTargetIds);
11421137
}
11431138

1144-
removeMutationReference(
1139+
markPotentiallyOrphaned(
11451140
txn: PersistenceTransaction,
11461141
key: DocumentKey
11471142
): PersistencePromise<void> {
@@ -1158,11 +1153,7 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
11581153
txn: PersistenceTransaction,
11591154
docKey: DocumentKey
11601155
): PersistencePromise<boolean> {
1161-
if (this.inMemoryPins!.containsKey(docKey)) {
1162-
return PersistencePromise.resolve<boolean>(true);
1163-
} else {
1164-
return mutationQueuesContainKey(txn, docKey);
1165-
}
1156+
return mutationQueuesContainKey(txn, docKey);
11661157
}
11671158

11681159
removeOrphanedDocuments(

packages/firestore/src/local/indexeddb_target_cache.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ export class IndexedDbTargetCache implements TargetCache {
284284
keys.forEach(key => {
285285
const path = encodeResourcePath(key.path);
286286
promises.push(store.put(new DbTargetDocument(targetId, path)));
287-
promises.push(this.referenceDelegate.addReference(txn, key));
287+
promises.push(this.referenceDelegate.addReference(txn, targetId, key));
288288
});
289289
return PersistencePromise.waitFor(promises);
290290
}
@@ -301,7 +301,7 @@ export class IndexedDbTargetCache implements TargetCache {
301301
const path = encodeResourcePath(key.path);
302302
return PersistencePromise.waitFor([
303303
store.delete([targetId, path]),
304-
this.referenceDelegate.removeReference(txn, key)
304+
this.referenceDelegate.removeReference(txn, targetId, key)
305305
]);
306306
});
307307
}

packages/firestore/src/local/local_store.ts

Lines changed: 46 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import {
5656
import { PersistencePromise } from './persistence_promise';
5757
import { TargetCache } from './target_cache';
5858
import { QueryEngine } from './query_engine';
59-
import { ReferenceSet } from './reference_set';
6059
import { RemoteDocumentCache } from './remote_document_cache';
6160
import { RemoteDocumentChangeBuffer } from './remote_document_change_buffer';
6261
import { ClientId } from './shared_client_state';
@@ -165,11 +164,6 @@ export class LocalStore {
165164
*/
166165
protected localDocuments: LocalDocumentsView;
167166

168-
/**
169-
* The set of document references maintained by any local views.
170-
*/
171-
private localViewReferences = new ReferenceSet();
172-
173167
/** Maps a target to its `TargetData`. */
174168
protected targetCache: TargetCache;
175169

@@ -206,9 +200,6 @@ export class LocalStore {
206200
persistence.started,
207201
'LocalStore was passed an unstarted persistence implementation'
208202
);
209-
this.persistence.referenceDelegate.setInMemoryPins(
210-
this.localViewReferences
211-
);
212203
this.mutationQueue = persistence.getMutationQueue(initialUser);
213204
this.remoteDocuments = persistence.getRemoteDocumentCache();
214205
this.targetCache = persistence.getTargetCache();
@@ -704,49 +695,56 @@ export class LocalStore {
704695
* Notify local store of the changed views to locally pin documents.
705696
*/
706697
notifyLocalViewChanges(viewChanges: LocalViewChanges[]): Promise<void> {
707-
for (const viewChange of viewChanges) {
708-
const targetId = viewChange.targetId;
709-
710-
this.localViewReferences.addReferences(viewChange.addedKeys, targetId);
711-
this.localViewReferences.removeReferences(
712-
viewChange.removedKeys,
713-
targetId
714-
);
715-
716-
if (!viewChange.fromCache) {
717-
const targetData = this.targetDataByTarget.get(targetId);
718-
debugAssert(
719-
targetData !== null,
720-
`Can't set limbo-free snapshot version for unknown target: ${targetId}`
721-
);
722-
723-
// Advance the last limbo free snapshot version
724-
const lastLimboFreeSnapshotVersion = targetData.snapshotVersion;
725-
const updatedTargetData = targetData.withLastLimboFreeSnapshotVersion(
726-
lastLimboFreeSnapshotVersion
727-
);
728-
this.targetDataByTarget = this.targetDataByTarget.insert(
729-
targetId,
730-
updatedTargetData
731-
);
732-
}
733-
}
734-
return this.persistence.runTransaction(
735-
'notifyLocalViewChanges',
736-
'readwrite',
737-
txn => {
698+
return this.persistence
699+
.runTransaction('notifyLocalViewChanges', 'readwrite', txn => {
738700
return PersistencePromise.forEach(
739701
viewChanges,
740702
(viewChange: LocalViewChanges) => {
741703
return PersistencePromise.forEach(
742-
viewChange.removedKeys,
704+
viewChange.addedKeys,
743705
(key: DocumentKey) =>
744-
this.persistence.referenceDelegate.removeReference(txn, key)
706+
this.persistence.referenceDelegate.addReference(
707+
txn,
708+
viewChange.targetId,
709+
key
710+
)
711+
).next(() =>
712+
PersistencePromise.forEach(
713+
viewChange.removedKeys,
714+
(key: DocumentKey) =>
715+
this.persistence.referenceDelegate.removeReference(
716+
txn,
717+
viewChange.targetId,
718+
key
719+
)
720+
)
745721
);
746722
}
747723
);
748-
}
749-
);
724+
})
725+
.then(() => {
726+
for (const viewChange of viewChanges) {
727+
const targetId = viewChange.targetId;
728+
729+
if (!viewChange.fromCache) {
730+
const targetData = this.targetDataByTarget.get(targetId);
731+
debugAssert(
732+
targetData !== null,
733+
`Can't set limbo-free snapshot version for unknown target: ${targetId}`
734+
);
735+
736+
// Advance the last limbo free snapshot version
737+
const lastLimboFreeSnapshotVersion = targetData.snapshotVersion;
738+
const updatedTargetData = targetData.withLastLimboFreeSnapshotVersion(
739+
lastLimboFreeSnapshotVersion
740+
);
741+
this.targetDataByTarget = this.targetDataByTarget.insert(
742+
targetId,
743+
updatedTargetData
744+
);
745+
}
746+
}
747+
});
750748
}
751749

752750
/**
@@ -869,26 +867,11 @@ export class LocalStore {
869867
const mode = keepPersistedTargetData ? 'readwrite' : 'readwrite-primary';
870868
return this.persistence
871869
.runTransaction('Release target', mode, txn => {
872-
// References for documents sent via Watch are automatically removed
873-
// when we delete a target's data from the reference delegate.
874-
// Since this does not remove references for locally mutated documents,
875-
// we have to remove the target associations for these documents
876-
// manually.
877-
// This operation needs to be run inside the transaction since EagerGC
878-
// uses the local view references during the transaction's commit.
879-
// Fortunately, the operation is safe to be re-run in case the
880-
// transaction fails since there are no side effects if the target has
881-
// already been removed.
882-
const removed = this.localViewReferences.removeReferencesForId(
883-
targetId
884-
);
885-
886870
if (!keepPersistedTargetData) {
887-
return PersistencePromise.forEach(removed, (key: DocumentKey) =>
888-
this.persistence.referenceDelegate.removeReference(txn, key)
889-
).next(() => {
890-
this.persistence.referenceDelegate.removeTarget(txn, targetData!);
891-
});
871+
return this.persistence.referenceDelegate.removeTarget(
872+
txn,
873+
targetData!
874+
);
892875
} else {
893876
return PersistencePromise.resolve();
894877
}

packages/firestore/src/local/memory_mutation_queue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ export class MemoryMutationQueue implements MutationQueue {
299299
return PersistencePromise.forEach(batch.mutations, (mutation: Mutation) => {
300300
const ref = new DocReference(mutation.key, batch.batchId);
301301
references = references.delete(ref);
302-
return this.referenceDelegate.removeMutationReference(
302+
return this.referenceDelegate.markPotentiallyOrphaned(
303303
transaction,
304304
mutation.key
305305
);

0 commit comments

Comments
 (0)