Skip to content

Commit 29276cc

Browse files
Make applyRemoteEvent idempotent
1 parent c3da39c commit 29276cc

9 files changed

+243
-200
lines changed

packages/firestore/src/local/indexeddb_index_manager.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { decode, encode } from './encoded_resource_path';
2222
import { IndexManager } from './index_manager';
2323
import { IndexedDbPersistence } from './indexeddb_persistence';
2424
import { DbCollectionParent, DbCollectionParentKey } from './indexeddb_schema';
25-
import { MemoryCollectionParentIndex } from './memory_index_manager';
2625
import { PersistenceTransaction } from './persistence';
2726
import { PersistencePromise } from './persistence_promise';
2827
import { SimpleDbStore } from './simple_db';
@@ -31,30 +30,17 @@ import { SimpleDbStore } from './simple_db';
3130
* A persisted implementation of IndexManager.
3231
*/
3332
export class IndexedDbIndexManager implements IndexManager {
34-
/**
35-
* An in-memory copy of the index entries we've already written since the SDK
36-
* launched. Used to avoid re-writing the same entry repeatedly.
37-
*
38-
* This is *NOT* a complete cache of what's in persistence and so can never be used to
39-
* satisfy reads.
40-
*/
41-
private collectionParentsCache = new MemoryCollectionParentIndex();
42-
4333
addToCollectionParentIndex(
4434
transaction: PersistenceTransaction,
4535
collectionPath: ResourcePath
4636
): PersistencePromise<void> {
4737
assert(collectionPath.length % 2 === 1, 'Expected a collection path.');
48-
if (this.collectionParentsCache.add(collectionPath)) {
49-
assert(collectionPath.length >= 1, 'Invalid collection path.');
50-
const collectionId = collectionPath.lastSegment();
51-
const parentPath = collectionPath.popLast();
52-
return collectionParentsStore(transaction).put({
53-
collectionId,
54-
parent: encode(parentPath)
55-
});
56-
}
57-
return PersistencePromise.resolve();
38+
const collectionId = collectionPath.lastSegment();
39+
const parentPath = collectionPath.popLast();
40+
return collectionParentsStore(transaction).put({
41+
collectionId,
42+
parent: encode(parentPath)
43+
});
5844
}
5945

6046
getCollectionParents(

packages/firestore/src/local/indexeddb_mutation_queue.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,23 +181,28 @@ export class IndexedDbMutationQueue implements MutationQueue {
181181
this.documentKeysByBatchId[batchId] = batch.keys();
182182

183183
const promises: Array<PersistencePromise<void>> = [];
184+
let collectionParents = new SortedSet<ResourcePath>((l, r) =>
185+
primitiveComparator(l.canonicalString(), r.canonicalString())
186+
);
184187
for (const mutation of mutations) {
185188
const indexKey = DbDocumentMutation.key(
186189
this.userId,
187190
mutation.key.path,
188191
batchId
189192
);
193+
collectionParents = collectionParents.add(mutation.key.path.popLast());
190194
promises.push(mutationStore.put(dbBatch));
191195
promises.push(
192196
documentStore.put(indexKey, DbDocumentMutation.PLACEHOLDER)
193197
);
198+
}
199+
200+
collectionParents.forEach(parent => {
194201
promises.push(
195-
this.indexManager.addToCollectionParentIndex(
196-
transaction,
197-
mutation.key.path.popLast()
198-
)
202+
this.indexManager.addToCollectionParentIndex(transaction, parent)
199203
);
200-
}
204+
});
205+
201206
return PersistencePromise.waitFor(promises).next(() => batch);
202207
});
203208
}

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,10 @@ export class IndexedDbPersistence implements Persistence {
764764
simpleDbMode,
765765
ALL_STORES,
766766
simpleDbTxn => {
767-
if (mode === 'readwrite-primary') {
767+
if (
768+
mode === 'readwrite-primary' ||
769+
mode === 'readwrite-primary-idempotent'
770+
) {
768771
// While we merely verify that we have (or can acquire) the lease
769772
// immediately, we wait to extend the primary lease until after
770773
// executing transactionOperation(). This ensures that even if the

packages/firestore/src/local/indexeddb_query_cache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export class IndexedDbQueryCache implements QueryCache {
164164
const queryData = this.serializer.fromDbTarget(value);
165165
if (
166166
queryData.sequenceNumber <= upperBound &&
167-
activeTargetIds[queryData.targetId] === undefined
167+
activeTargetIds.get(queryData.targetId) === null
168168
) {
169169
count++;
170170
promises.push(this.removeQueryData(txn, queryData));

packages/firestore/src/local/indexeddb_remote_document_cache.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ import {
2929
} from '../model/collections';
3030
import { Document, MaybeDocument, NoDocument } from '../model/document';
3131
import { DocumentKey } from '../model/document_key';
32+
import { ResourcePath } from '../model/path';
33+
import { primitiveComparator } from '../util/misc';
3234
import { SortedMap } from '../util/sorted_map';
35+
import { SortedSet } from '../util/sorted_set';
3336

3437
import { SnapshotVersion } from '../core/snapshot_version';
3538
import { assert, fail } from '../util/assert';
@@ -91,12 +94,7 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
9194
doc: DbRemoteDocument
9295
): PersistencePromise<void> {
9396
const documentStore = remoteDocumentsStore(transaction);
94-
return documentStore.put(dbKey(key), doc).next(() => {
95-
this.indexManager.addToCollectionParentIndex(
96-
transaction,
97-
key.path.popLast()
98-
);
99-
});
97+
return documentStore.put(dbKey(key), doc);
10098
}
10199

102100
/**
@@ -454,6 +452,10 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
454452

455453
let sizeDelta = 0;
456454

455+
let collectionParents = new SortedSet<ResourcePath>((l, r) =>
456+
primitiveComparator(l.canonicalString(), r.canonicalString())
457+
);
458+
457459
this.changes.forEach((key, maybeDocument) => {
458460
const previousSize = this.documentSizes.get(key);
459461
assert(
@@ -469,6 +471,8 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
469471
maybeDocument,
470472
this.readTime
471473
);
474+
collectionParents = collectionParents.add(key.path.popLast());
475+
472476
const size = dbDocumentSize(doc);
473477
sizeDelta += size - previousSize!;
474478
promises.push(this.documentCache.addEntry(transaction, key, doc));
@@ -492,6 +496,15 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
492496
}
493497
});
494498

499+
collectionParents.forEach(parent => {
500+
promises.push(
501+
this.documentCache.indexManager.addToCollectionParentIndex(
502+
transaction,
503+
parent
504+
)
505+
);
506+
});
507+
495508
promises.push(this.documentCache.updateMetadata(transaction, sizeDelta));
496509

497510
return PersistencePromise.waitFor(promises);

0 commit comments

Comments
 (0)