Skip to content

Commit 1e68233

Browse files
committed
Port changes from Android SDK PR#3420.
Note that we are not going to do any processing in the background.
1 parent ec8fe08 commit 1e68233

File tree

6 files changed

+134
-45
lines changed

6 files changed

+134
-45
lines changed

packages/firestore/src/local/document_overlay_cache.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ export interface DocumentOverlayCache {
4343
key: DocumentKey
4444
): PersistencePromise<Overlay | null>;
4545

46+
/**
47+
* Gets the saved overlay mutation for the given document keys. Skips keys for
48+
* which there are no overlays.
49+
*/
50+
getOverlays(
51+
transaction: PersistenceTransaction,
52+
keys: DocumentKeySet
53+
): PersistencePromise<OverlayMap>;
54+
4655
/**
4756
* Saves the given document mutation map to persistence as overlays.
4857
* All overlays will have their largest batch id set to `largestBatchId`.

packages/firestore/src/local/indexeddb_document_overlay_cache.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,24 @@ export class IndexedDbDocumentOverlayCache implements DocumentOverlayCache {
8181
});
8282
}
8383

84+
getOverlays(
85+
transaction: PersistenceTransaction,
86+
keys: DocumentKeySet
87+
): PersistencePromise<OverlayMap> {
88+
const result = newOverlayMap();
89+
const promises: Array<PersistencePromise<void>> = [];
90+
keys.forEach(key => {
91+
promises.push(
92+
this.getOverlay(transaction, key).next(overlay => {
93+
if (overlay !== null) {
94+
result.set(key, overlay);
95+
}
96+
})
97+
);
98+
});
99+
return PersistencePromise.waitFor(promises).next(() => result);
100+
}
101+
84102
saveOverlays(
85103
transaction: PersistenceTransaction,
86104
largestBatchId: number,

packages/firestore/src/local/local_documents_view.ts

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,39 @@ export class LocalDocumentsView {
134134
docs: MutableDocumentMap,
135135
existenceStateChanged: DocumentKeySet
136136
): PersistencePromise<DocumentMap> {
137-
return this.computeViews(
138-
transaction,
139-
docs,
140-
newOverlayMap(),
141-
existenceStateChanged
142-
);
137+
const overlays = newOverlayMap();
138+
return this.populateOverlays(transaction, overlays, docs).next(() => {
139+
return this.computeViews(
140+
transaction,
141+
docs,
142+
overlays,
143+
existenceStateChanged
144+
);
145+
});
146+
}
147+
148+
/**
149+
* Fetches the overlays for {@code docs} and adds them to provided overlay map
150+
* if the map does not already contain an entry for the given document key.
151+
*/
152+
private populateOverlays(
153+
transaction: PersistenceTransaction,
154+
overlays: OverlayMap,
155+
docs: MutableDocumentMap
156+
): PersistencePromise<void> {
157+
let missingOverlays = documentKeySet();
158+
docs.forEach(key => {
159+
if (!overlays.has(key)) {
160+
missingOverlays = missingOverlays.add(key);
161+
}
162+
});
163+
return this.documentOverlayCache
164+
.getOverlays(transaction, missingOverlays)
165+
.next(result => {
166+
result.forEach((key, val) => {
167+
overlays.set(key, val);
168+
});
169+
});
143170
}
144171

145172
/**
@@ -149,53 +176,39 @@ export class LocalDocumentsView {
149176
computeViews(
150177
transaction: PersistenceTransaction,
151178
docs: MutableDocumentMap,
152-
memoizedOverlays: OverlayMap,
179+
overlays: OverlayMap,
153180
existenceStateChanged: DocumentKeySet
154181
): PersistencePromise<DocumentMap> {
155182
let results = documentMap();
156183
let recalculateDocuments = mutableDocumentMap();
157-
const promises: Array<PersistencePromise<void>> = [];
158184
docs.forEach((_, doc) => {
159-
const overlayPromise = memoizedOverlays.has(doc.key)
160-
? PersistencePromise.resolve(memoizedOverlays.get(doc.key)!)
161-
: this.documentOverlayCache.getOverlay(transaction, doc.key);
162-
163-
promises.push(
164-
overlayPromise.next((overlay: Overlay | null) => {
165-
// Recalculate an overlay if the document's existence state is changed
166-
// due to a remote event *and* the overlay is a PatchMutation. This is
167-
// because document existence state can change if some patch mutation's
168-
// preconditions are met.
169-
// NOTE: we recalculate when `overlay` is null as well, because there
170-
// might be a patch mutation whose precondition does not match before
171-
// the change (hence overlay==null), but would now match.
172-
if (
173-
existenceStateChanged.has(doc.key) &&
174-
(overlay === null || overlay.mutation instanceof PatchMutation)
175-
) {
176-
recalculateDocuments = recalculateDocuments.insert(doc.key, doc);
177-
} else if (overlay !== null) {
178-
mutationApplyToLocalView(
179-
overlay.mutation,
180-
doc,
181-
null,
182-
Timestamp.now()
183-
);
184-
}
185-
})
186-
);
185+
const overlay = overlays.get(doc.key);
186+
// Recalculate an overlay if the document's existence state is changed due
187+
// to a remote event *and* the overlay is a PatchMutation. This is because
188+
// document existence state can change if some patch mutation's
189+
// preconditions are met.
190+
// NOTE: we recalculate when `overlay` is undefined as well, because there
191+
// might be a patch mutation whose precondition does not match before the
192+
// change (hence overlay is undefined), but would now match.
193+
if (
194+
existenceStateChanged.has(doc.key) &&
195+
(overlay === undefined || overlay.mutation instanceof PatchMutation)
196+
) {
197+
recalculateDocuments = recalculateDocuments.insert(doc.key, doc);
198+
} else if (overlay !== undefined) {
199+
mutationApplyToLocalView(overlay.mutation, doc, null, Timestamp.now());
200+
}
187201
});
188202

189-
return PersistencePromise.waitFor(promises)
190-
.next(() =>
191-
this.recalculateAndSaveOverlays(transaction, recalculateDocuments)
192-
)
193-
.next(() => {
194-
docs.forEach((key, value) => {
195-
results = results.insert(key, value);
196-
});
197-
return results;
203+
return this.recalculateAndSaveOverlays(
204+
transaction,
205+
recalculateDocuments
206+
).next(() => {
207+
docs.forEach((key, value) => {
208+
results = results.insert(key, value);
198209
});
210+
return results;
211+
});
199212
}
200213

201214
private recalculateAndSaveOverlays(

packages/firestore/src/local/memory_document_overlay_cache.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
5050
return PersistencePromise.resolve(this.overlays.get(key));
5151
}
5252

53+
getOverlays(
54+
transaction: PersistenceTransaction,
55+
keys: DocumentKeySet
56+
): PersistencePromise<OverlayMap> {
57+
const result = newOverlayMap();
58+
const promises: Array<PersistencePromise<void>> = [];
59+
keys.forEach(key => {
60+
promises.push(
61+
this.getOverlay(transaction, key).next(overlay => {
62+
if (overlay !== null) {
63+
result.set(key, overlay);
64+
}
65+
})
66+
);
67+
});
68+
return PersistencePromise.waitFor(promises).next(() => result);
69+
}
70+
5371
saveOverlays(
5472
transaction: PersistenceTransaction,
5573
largestBatchId: number,

packages/firestore/test/unit/local/document_overlay_cache.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,4 +327,29 @@ function genericDocumentOverlayCacheTests(): void {
327327
);
328328
expect(await overlayCache.getOverlay(key('coll/doc'))).to.equal(null);
329329
});
330+
331+
it('skips non-existing overlay in batch lookup', async () => {
332+
const result = await overlayCache.getOverlays(
333+
documentKeySet(key('coll/doc1'))
334+
);
335+
expect(result.isEmpty()).to.equal(true);
336+
});
337+
338+
it('supports empty batch in batch lookup', async () => {
339+
const result = await overlayCache.getOverlays(documentKeySet());
340+
expect(result.isEmpty()).to.equal(true);
341+
});
342+
343+
it('can read saved overlays in batches', async () => {
344+
const m1 = setMutation('coll/a', { 'a': 1 });
345+
const m2 = setMutation('coll/b', { 'b': 2 });
346+
const m3 = setMutation('coll/c', { 'c': 3 });
347+
await saveOverlaysForMutations(3, m1, m2, m3);
348+
const overlays = await overlayCache.getOverlays(
349+
documentKeySet(key('coll/a'), key('coll/b'), key('coll/c'))
350+
);
351+
verifyEqualMutations(overlays.get(key('coll/a'))!.mutation, m1);
352+
verifyEqualMutations(overlays.get(key('coll/b'))!.mutation, m2);
353+
verifyEqualMutations(overlays.get(key('coll/c'))!.mutation, m3);
354+
});
330355
}

packages/firestore/test/unit/local/test_document_overlay_cache.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ export class TestDocumentOverlayCache {
5050
});
5151
}
5252

53+
getOverlays(keys: DocumentKeySet): Promise<OverlayMap> {
54+
return this.persistence.runTransaction('getOverlays', 'readonly', txn => {
55+
return this.cache.getOverlays(txn, keys);
56+
});
57+
}
58+
5359
getOverlayMutation(docKey: string): Promise<Mutation | null> {
5460
return this.getOverlay(key(docKey)).then(value => {
5561
if (!value) {

0 commit comments

Comments
 (0)