Skip to content

Commit 6b3fc3f

Browse files
Make notifyLocalViewChanges idempotent
1 parent a57dac5 commit 6b3fc3f

13 files changed

+121
-121
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 & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,8 +1070,6 @@ function clientMetadataStore(
10701070

10711071
/** Provides LRU functionality for IndexedDB persistence. */
10721072
export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
1073-
private inMemoryPins: ReferenceSet | null = null;
1074-
10751073
readonly garbageCollector: LruGarbageCollector;
10761074

10771075
constructor(private readonly db: IndexedDbPersistence, params: LruParams) {
@@ -1081,14 +1079,14 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
10811079
getSequenceNumberCount(
10821080
txn: PersistenceTransaction
10831081
): PersistencePromise<number> {
1084-
const docCountPromise = this.orphanedDocmentCount(txn);
1082+
const docCountPromise = this.orphanedDocumentCount(txn);
10851083
const targetCountPromise = this.db.getTargetCache().getTargetCount(txn);
10861084
return targetCountPromise.next(targetCount =>
10871085
docCountPromise.next(docCount => targetCount + docCount)
10881086
);
10891087
}
10901088

1091-
private orphanedDocmentCount(
1089+
private orphanedDocumentCount(
10921090
txn: PersistenceTransaction
10931091
): PersistencePromise<number> {
10941092
let orphanedCount = 0;
@@ -1113,19 +1111,17 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
11131111
);
11141112
}
11151113

1116-
setInMemoryPins(inMemoryPins: ReferenceSet): void {
1117-
this.inMemoryPins = inMemoryPins;
1118-
}
1119-
11201114
addReference(
11211115
txn: PersistenceTransaction,
1116+
targetId: TargetId,
11221117
key: DocumentKey
11231118
): PersistencePromise<void> {
11241119
return writeSentinelKey(txn, key);
11251120
}
11261121

11271122
removeReference(
11281123
txn: PersistenceTransaction,
1124+
targetId: TargetId,
11291125
key: DocumentKey
11301126
): PersistencePromise<void> {
11311127
return writeSentinelKey(txn, key);
@@ -1141,7 +1137,7 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
11411137
.removeTargets(txn, upperBound, activeTargetIds);
11421138
}
11431139

1144-
removeMutationReference(
1140+
markPotentiallyOrphaned(
11451141
txn: PersistenceTransaction,
11461142
key: DocumentKey
11471143
): PersistencePromise<void> {
@@ -1158,11 +1154,7 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
11581154
txn: PersistenceTransaction,
11591155
docKey: DocumentKey
11601156
): PersistencePromise<boolean> {
1161-
if (this.inMemoryPins!.containsKey(docKey)) {
1162-
return PersistencePromise.resolve<boolean>(true);
1163-
} else {
1164-
return mutationQueuesContainKey(txn, docKey);
1165-
}
1157+
return mutationQueuesContainKey(txn, docKey);
11661158
}
11671159

11681160
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 & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,6 @@ export class LocalStore {
165165
*/
166166
protected localDocuments: LocalDocumentsView;
167167

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

@@ -206,9 +201,6 @@ export class LocalStore {
206201
persistence.started,
207202
'LocalStore was passed an unstarted persistence implementation'
208203
);
209-
this.persistence.referenceDelegate.setInMemoryPins(
210-
this.localViewReferences
211-
);
212204
this.mutationQueue = persistence.getMutationQueue(initialUser);
213205
this.remoteDocuments = persistence.getRemoteDocumentCache();
214206
this.targetCache = persistence.getTargetCache();
@@ -704,49 +696,56 @@ export class LocalStore {
704696
* Notify local store of the changed views to locally pin documents.
705697
*/
706698
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 => {
699+
return this.persistence
700+
.runTransaction('notifyLocalViewChanges', 'readwrite', txn => {
738701
return PersistencePromise.forEach(
739702
viewChanges,
740703
(viewChange: LocalViewChanges) => {
741704
return PersistencePromise.forEach(
742-
viewChange.removedKeys,
705+
viewChange.addedKeys,
743706
(key: DocumentKey) =>
744-
this.persistence.referenceDelegate.removeReference(txn, key)
707+
this.persistence.referenceDelegate.addReference(
708+
txn,
709+
viewChange.targetId,
710+
key
711+
)
712+
).next(() =>
713+
PersistencePromise.forEach(
714+
viewChange.removedKeys,
715+
(key: DocumentKey) =>
716+
this.persistence.referenceDelegate.removeReference(
717+
txn,
718+
viewChange.targetId,
719+
key
720+
)
721+
)
745722
);
746723
}
747724
);
748-
}
749-
);
725+
})
726+
.then(() => {
727+
for (const viewChange of viewChanges) {
728+
const targetId = viewChange.targetId;
729+
730+
if (!viewChange.fromCache) {
731+
const targetData = this.targetDataByTarget.get(targetId);
732+
debugAssert(
733+
targetData !== null,
734+
`Can't set limbo-free snapshot version for unknown target: ${targetId}`
735+
);
736+
737+
// Advance the last limbo free snapshot version
738+
const lastLimboFreeSnapshotVersion = targetData.snapshotVersion;
739+
const updatedTargetData = targetData.withLastLimboFreeSnapshotVersion(
740+
lastLimboFreeSnapshotVersion
741+
);
742+
this.targetDataByTarget = this.targetDataByTarget.insert(
743+
targetId,
744+
updatedTargetData
745+
);
746+
}
747+
}
748+
});
750749
}
751750

752751
/**
@@ -869,26 +868,11 @@ export class LocalStore {
869868
const mode = keepPersistedTargetData ? 'readwrite' : 'readwrite-primary';
870869
return this.persistence
871870
.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-
886871
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-
});
872+
return this.persistence.referenceDelegate.removeTarget(
873+
txn,
874+
targetData!
875+
);
892876
} else {
893877
return PersistencePromise.resolve();
894878
}

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
);

packages/firestore/src/local/memory_persistence.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
LruParams
3030
} from './lru_garbage_collector';
3131
import { ListenSequence } from '../core/listen_sequence';
32-
import { ListenSequenceNumber } from '../core/types';
32+
import { ListenSequenceNumber, TargetId } from '../core/types';
3333
import { estimateByteSize } from '../model/values';
3434
import { MemoryIndexManager } from './memory_index_manager';
3535
import { MemoryMutationQueue } from './memory_mutation_queue';
@@ -184,7 +184,9 @@ export interface MemoryReferenceDelegate extends ReferenceDelegate {
184184
}
185185

186186
export class MemoryEagerDelegate implements MemoryReferenceDelegate {
187-
private inMemoryPins: ReferenceSet | null = null;
187+
/** Manages all documents that are active in Query views. */
188+
private localViewReferences: ReferenceSet = new ReferenceSet();
189+
/** The list of documents that are potentially GCed after each transaction. */
188190
private _orphanedDocuments: Set<DocumentKey> | null = null;
189191

190192
private constructor(private readonly persistence: MemoryPersistence) {}
@@ -201,27 +203,27 @@ export class MemoryEagerDelegate implements MemoryReferenceDelegate {
201203
}
202204
}
203205

204-
setInMemoryPins(inMemoryPins: ReferenceSet): void {
205-
this.inMemoryPins = inMemoryPins;
206-
}
207-
208206
addReference(
209207
txn: PersistenceTransaction,
208+
targetId: TargetId,
210209
key: DocumentKey
211210
): PersistencePromise<void> {
211+
this.localViewReferences.addReference(key, targetId);
212212
this.orphanedDocuments.delete(key);
213213
return PersistencePromise.resolve();
214214
}
215215

216216
removeReference(
217217
txn: PersistenceTransaction,
218+
targetId: TargetId,
218219
key: DocumentKey
219220
): PersistencePromise<void> {
221+
this.localViewReferences.removeReference(key, targetId);
220222
this.orphanedDocuments.add(key);
221223
return PersistencePromise.resolve();
222224
}
223225

224-
removeMutationReference(
226+
markPotentiallyOrphaned(
225227
txn: PersistenceTransaction,
226228
key: DocumentKey
227229
): PersistencePromise<void> {
@@ -233,6 +235,10 @@ export class MemoryEagerDelegate implements MemoryReferenceDelegate {
233235
txn: PersistenceTransaction,
234236
targetData: TargetData
235237
): PersistencePromise<void> {
238+
const orphaned = this.localViewReferences.removeReferencesForId(
239+
targetData.targetId
240+
);
241+
orphaned.forEach(key => this.orphanedDocuments.add(key));
236242
const cache = this.persistence.getTargetCache();
237243
return cache
238244
.getMatchingKeysForTargetId(txn, targetData.targetId)
@@ -290,15 +296,15 @@ export class MemoryEagerDelegate implements MemoryReferenceDelegate {
290296
key: DocumentKey
291297
): PersistencePromise<boolean> {
292298
return PersistencePromise.or([
299+
() =>
300+
PersistencePromise.resolve(this.localViewReferences.containsKey(key)),
293301
() => this.persistence.getTargetCache().containsKey(txn, key),
294-
() => this.persistence.mutationQueuesContainKey(txn, key),
295-
() => PersistencePromise.resolve(this.inMemoryPins!.containsKey(key))
302+
() => this.persistence.mutationQueuesContainKey(txn, key)
296303
]);
297304
}
298305
}
299306

300307
export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
301-
private inMemoryPins: ReferenceSet | null = null;
302308
private orphanedSequenceNumbers: ObjectMap<
303309
DocumentKey,
304310
ListenSequenceNumber
@@ -371,10 +377,6 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
371377
);
372378
}
373379

374-
setInMemoryPins(inMemoryPins: ReferenceSet): void {
375-
this.inMemoryPins = inMemoryPins;
376-
}
377-
378380
removeTargets(
379381
txn: PersistenceTransaction,
380382
upperBound: ListenSequenceNumber,
@@ -403,7 +405,7 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
403405
return p.next(() => changeBuffer.apply(txn)).next(() => count);
404406
}
405407

406-
removeMutationReference(
408+
markPotentiallyOrphaned(
407409
txn: PersistenceTransaction,
408410
key: DocumentKey
409411
): PersistencePromise<void> {
@@ -421,6 +423,7 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
421423

422424
addReference(
423425
txn: PersistenceTransaction,
426+
targetId: TargetId,
424427
key: DocumentKey
425428
): PersistencePromise<void> {
426429
this.orphanedSequenceNumbers.set(key, txn.currentSequenceNumber);
@@ -429,6 +432,7 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
429432

430433
removeReference(
431434
txn: PersistenceTransaction,
435+
targetId: TargetId,
432436
key: DocumentKey
433437
): PersistencePromise<void> {
434438
this.orphanedSequenceNumbers.set(key, txn.currentSequenceNumber);
@@ -458,7 +462,6 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
458462
): PersistencePromise<boolean> {
459463
return PersistencePromise.or([
460464
() => this.persistence.mutationQueuesContainKey(txn, key),
461-
() => PersistencePromise.resolve(this.inMemoryPins!.containsKey(key)),
462465
() => this.persistence.getTargetCache().containsKey(txn, key),
463466
() => {
464467
const orphanedAt = this.orphanedSequenceNumbers.get(key);

0 commit comments

Comments
 (0)