Skip to content

Commit e3cbe77

Browse files
authored
Merge 8feb735 into c4395f2
2 parents c4395f2 + 8feb735 commit e3cbe77

File tree

8 files changed

+104
-6
lines changed

8 files changed

+104
-6
lines changed

firebase-firestore/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Unreleased
2+
* [fixed] Fix an issue that stops some performance optimization being applied.
23

34
# 24.4.1
45
* [fixed] Fix `FAILED_PRECONDITION` when writing to a deleted document in a

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalDocumentsView.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ private Map<DocumentKey, OverlayedDocument> computeViews(
160160
Map<DocumentKey, FieldMask> mutatedFields = new HashMap<>();
161161
for (MutableDocument doc : docs.values()) {
162162
Overlay overlay = overlays.get(doc.getKey());
163+
163164
// Recalculate an overlay if the document's existence state is changed due to a remote
164165
// event *and* the overlay is a PatchMutation. This is because document existence state
165166
// can change if some patch mutation's preconditions are met.
@@ -174,6 +175,9 @@ private Map<DocumentKey, OverlayedDocument> computeViews(
174175
overlay
175176
.getMutation()
176177
.applyToLocalView(doc, overlay.getMutation().getFieldMask(), Timestamp.now());
178+
} else { // overlay == null
179+
// Using EMPTY to indicate there is no overlay for the document.
180+
mutatedFields.put(doc.getKey(), FieldMask.EMPTY);
177181
}
178182
}
179183

firebase-firestore/src/main/java/com/google/firebase/firestore/local/OverlayedDocument.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
/** Represents a local view (overlay) of a document, and the fields that are locally mutated. */
2222
public class OverlayedDocument {
2323
private Document overlayedDocument;
24-
private FieldMask mutatedFields;
24+
@Nullable private FieldMask mutatedFields;
2525

2626
OverlayedDocument(Document overlayedDocument, FieldMask mutatedFields) {
2727
this.overlayedDocument = overlayedDocument;
@@ -33,8 +33,11 @@ public Document getDocument() {
3333
}
3434

3535
/**
36-
* The fields that are locally mutated by patch mutations. If the overlayed document is from set
37-
* or delete mutations, this returns null.
36+
* The fields that are locally mutated by patch mutations.
37+
*
38+
* <p>If the overlayed document is from set or delete mutations, returns null.
39+
*
40+
* <p>If there is no overlay (mutation) for the document, returns FieldMask.EMPTY.
3841
*/
3942
public @Nullable FieldMask getMutatedFields() {
4043
return mutatedFields;

firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/MutationBatch.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ public Map<DocumentKey, Mutation> applyToLocalDocumentSet(
142142
for (DocumentKey key : getKeys()) {
143143
// TODO(mutabledocuments): This method should take a map of MutableDocuments and we should
144144
// remove this cast.
145-
MutableDocument document = (MutableDocument) documentMap.get(key).getDocument();
145+
OverlayedDocument overlayedDocument = documentMap.get(key);
146+
MutableDocument document = (MutableDocument) overlayedDocument.getDocument();
146147
FieldMask mutatedFields = applyToLocalView(document, documentMap.get(key).getMutatedFields());
147148
// Set mutationFields to null if the document is only from local mutations, this creates
148149
// a Set(or Delete) mutation, instead of trying to create a patch mutation as the overlay.
@@ -151,6 +152,7 @@ public Map<DocumentKey, Mutation> applyToLocalDocumentSet(
151152
if (overlay != null) {
152153
overlays.put(key, overlay);
153154
}
155+
154156
if (!document.isValidDocument()) {
155157
document.convertToNoDocument(SnapshotVersion.NONE);
156158
}

firebase-firestore/src/test/java/com/google/firebase/firestore/local/CountingQueryEngine.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@
2424
import com.google.firebase.firestore.model.MutableDocument;
2525
import com.google.firebase.firestore.model.ResourcePath;
2626
import com.google.firebase.firestore.model.SnapshotVersion;
27+
import com.google.firebase.firestore.model.mutation.DeleteMutation;
2728
import com.google.firebase.firestore.model.mutation.Mutation;
2829
import com.google.firebase.firestore.model.mutation.Overlay;
30+
import com.google.firebase.firestore.model.mutation.PatchMutation;
31+
import com.google.firebase.firestore.model.mutation.SetMutation;
2932
import java.util.Collection;
33+
import java.util.Collections;
34+
import java.util.HashMap;
3035
import java.util.Map;
3136
import java.util.SortedSet;
3237

@@ -35,10 +40,17 @@
3540
* mutations read.
3641
*/
3742
class CountingQueryEngine extends QueryEngine {
43+
enum OverlayType {
44+
Patch,
45+
Set,
46+
Delete
47+
}
48+
3849
private final QueryEngine queryEngine;
3950

4051
private final int[] overlaysReadByCollection = new int[] {0};
4152
private final int[] overlaysReadByKey = new int[] {0};
53+
private final Map<DocumentKey, OverlayType> overlayTypes = new HashMap();
4254
private final int[] documentsReadByCollection = new int[] {0};
4355
private final int[] documentsReadByKey = new int[] {0};
4456

@@ -49,6 +61,7 @@ class CountingQueryEngine extends QueryEngine {
4961
void resetCounts() {
5062
overlaysReadByCollection[0] = 0;
5163
overlaysReadByKey[0] = 0;
64+
overlayTypes.clear();
5265
documentsReadByCollection[0] = 0;
5366
documentsReadByKey[0] = 0;
5467
}
@@ -104,6 +117,14 @@ int getOverlaysReadByKey() {
104117
return overlaysReadByKey[0];
105118
}
106119

120+
/**
121+
* Returns the types of overlay returned by the OverlayCahce's `getOverlays()` API (since the last
122+
* call to `resetCounts()`)
123+
*/
124+
Map<DocumentKey, OverlayType> getOverlayTypes() {
125+
return Collections.unmodifiableMap(overlayTypes);
126+
}
127+
107128
private RemoteDocumentCache wrapRemoteDocumentCache(RemoteDocumentCache subject) {
108129
return new RemoteDocumentCache() {
109130
@Override
@@ -160,12 +181,19 @@ private DocumentOverlayCache wrapOverlayCache(DocumentOverlayCache subject) {
160181
@Override
161182
public Overlay getOverlay(DocumentKey key) {
162183
++overlaysReadByKey[0];
163-
return subject.getOverlay(key);
184+
Overlay overlay = subject.getOverlay(key);
185+
overlayTypes.put(key, getOverlayType(overlay));
186+
return overlay;
164187
}
165188

166189
public Map<DocumentKey, Overlay> getOverlays(SortedSet<DocumentKey> keys) {
167190
overlaysReadByKey[0] += keys.size();
168-
return subject.getOverlays(keys);
191+
Map<DocumentKey, Overlay> overlays = subject.getOverlays(keys);
192+
for (Map.Entry<DocumentKey, Overlay> entry : overlays.entrySet()) {
193+
overlayTypes.put(entry.getKey(), getOverlayType(entry.getValue()));
194+
}
195+
196+
return overlays;
169197
}
170198

171199
@Override
@@ -182,6 +210,9 @@ public void removeOverlaysForBatchId(int batchId) {
182210
public Map<DocumentKey, Overlay> getOverlays(ResourcePath collection, int sinceBatchId) {
183211
Map<DocumentKey, Overlay> result = subject.getOverlays(collection, sinceBatchId);
184212
overlaysReadByCollection[0] += result.size();
213+
for (Map.Entry<DocumentKey, Overlay> entry : result.entrySet()) {
214+
overlayTypes.put(entry.getKey(), getOverlayType(entry.getValue()));
215+
}
185216
return result;
186217
}
187218

@@ -191,8 +222,23 @@ public Map<DocumentKey, Overlay> getOverlays(
191222
Map<DocumentKey, Overlay> result =
192223
subject.getOverlays(collectionGroup, sinceBatchId, count);
193224
overlaysReadByCollection[0] += result.size();
225+
for (Map.Entry<DocumentKey, Overlay> entry : result.entrySet()) {
226+
overlayTypes.put(entry.getKey(), getOverlayType(entry.getValue()));
227+
}
194228
return result;
195229
}
230+
231+
private OverlayType getOverlayType(Overlay overlay) {
232+
if (overlay.getMutation() instanceof SetMutation) {
233+
return OverlayType.Set;
234+
} else if (overlay.getMutation() instanceof PatchMutation) {
235+
return OverlayType.Patch;
236+
} else if (overlay.getMutation() instanceof DeleteMutation) {
237+
return OverlayType.Delete;
238+
} else {
239+
throw new IllegalStateException("Overlay is a unrecognizable mutation.");
240+
}
241+
}
196242
};
197243
}
198244
}

firebase-firestore/src/test/java/com/google/firebase/firestore/local/LocalStoreTestCase.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static com.google.firebase.firestore.testutil.TestUtil.existenceFilterEvent;
2525
import static com.google.firebase.firestore.testutil.TestUtil.filter;
2626
import static com.google.firebase.firestore.testutil.TestUtil.key;
27+
import static com.google.firebase.firestore.testutil.TestUtil.keyMap;
2728
import static com.google.firebase.firestore.testutil.TestUtil.keySet;
2829
import static com.google.firebase.firestore.testutil.TestUtil.keys;
2930
import static com.google.firebase.firestore.testutil.TestUtil.map;
@@ -82,6 +83,7 @@
8283
import java.util.Collection;
8384
import java.util.Collections;
8485
import java.util.List;
86+
import java.util.Map;
8587
import java.util.Map.Entry;
8688
import java.util.Set;
8789
import java.util.stream.Collectors;
@@ -332,6 +334,11 @@ protected void assertOverlaysRead(int byKey, int byCollection) {
332334
"Overlays read (by collection)", byCollection, queryEngine.getOverlaysReadByCollection());
333335
}
334336

337+
/** Asserts the expected overlay types. */
338+
protected void assertOverlayTypes(Map<DocumentKey, CountingQueryEngine.OverlayType> expected) {
339+
assertEquals("Overlays types", expected, queryEngine.getOverlayTypes());
340+
}
341+
335342
/**
336343
* Asserts the expected numbers of documents read by the RemoteDocumentCache since the last call
337344
* to `resetPersistenceStats()`.
@@ -975,6 +982,7 @@ public void testReadsAllDocumentsForInitialCollectionQueries() {
975982
localStore.executeQuery(query, /* usePreviousResults= */ true);
976983
assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2);
977984
assertOverlaysRead(/* byKey= */ 0, /* byCollection= */ 1);
985+
assertOverlayTypes(keyMap("foo/bonk", CountingQueryEngine.OverlayType.Set));
978986
}
979987

980988
@Test
@@ -1689,4 +1697,21 @@ public void testMultipleFieldPatchesOnLocalDocs() {
16891697
assertChanged(doc("foo/bar", 0, map("likes", 1, "stars", 2)).setHasLocalMutations());
16901698
assertContains(doc("foo/bar", 0, map("likes", 1, "stars", 2)).setHasLocalMutations());
16911699
}
1700+
1701+
@Test
1702+
public void testUpdateOnRemoteDocLeadsToUpdateOverlay() {
1703+
Query query = query("foo");
1704+
allocateQuery(query);
1705+
1706+
applyRemoteEvent(updateRemoteEvent(doc("foo/baz", 10, map("a", 1)), asList(2), emptyList()));
1707+
applyRemoteEvent(updateRemoteEvent(doc("foo/bar", 20, map()), asList(2), emptyList()));
1708+
writeMutation(patchMutation("foo/baz", map("b", 2)));
1709+
1710+
resetPersistenceStats();
1711+
1712+
localStore.executeQuery(query, /* usePreviousResults= */ true);
1713+
assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2);
1714+
assertOverlaysRead(/* byKey= */ 0, /* byCollection= */ 1);
1715+
assertOverlayTypes(keyMap("foo/baz", CountingQueryEngine.OverlayType.Patch));
1716+
}
16921717
}

firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteLocalStoreTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static com.google.firebase.firestore.testutil.TestUtil.fieldIndex;
2323
import static com.google.firebase.firestore.testutil.TestUtil.filter;
2424
import static com.google.firebase.firestore.testutil.TestUtil.key;
25+
import static com.google.firebase.firestore.testutil.TestUtil.keyMap;
2526
import static com.google.firebase.firestore.testutil.TestUtil.map;
2627
import static com.google.firebase.firestore.testutil.TestUtil.orderBy;
2728
import static com.google.firebase.firestore.testutil.TestUtil.query;
@@ -209,6 +210,12 @@ public void testUsesPartiallyIndexedOverlaysWhenAvailable() {
209210
Query query = query("coll").filter(filter("matches", "==", true));
210211
executeQuery(query);
211212
assertOverlaysRead(/* byKey= */ 1, /* byCollection= */ 1);
213+
assertOverlayTypes(
214+
keyMap(
215+
"coll/a",
216+
CountingQueryEngine.OverlayType.Set,
217+
"coll/b",
218+
CountingQueryEngine.OverlayType.Set));
212219
assertQueryReturned("coll/a", "coll/b");
213220
}
214221

@@ -238,6 +245,7 @@ public void testDoesNotUseLimitWhenIndexIsOutdated() {
238245
// The query engine first reads the documents by key and then re-runs the query without limit.
239246
assertRemoteDocumentsRead(/* byKey= */ 5, /* byCollection= */ 0);
240247
assertOverlaysRead(/* byKey= */ 5, /* byCollection= */ 1);
248+
assertOverlayTypes(keyMap("coll/b", CountingQueryEngine.OverlayType.Delete));
241249
assertQueryReturned("coll/a", "coll/c");
242250
}
243251

@@ -279,6 +287,7 @@ public void testIndexesServerTimestamps() {
279287
Query query = query("coll").orderBy(orderBy("time", "asc"));
280288
executeQuery(query);
281289
assertOverlaysRead(/* byKey= */ 1, /* byCollection= */ 0);
290+
assertOverlayTypes(keyMap("coll/a", CountingQueryEngine.OverlayType.Set));
282291
assertQueryReturned("coll/a");
283292
}
284293
}

firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestUtil.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,14 @@ public static ImmutableSortedSet<DocumentKey> keySet(DocumentKey... keys) {
254254
return keySet;
255255
}
256256

257+
public static <T> Map<DocumentKey, T> keyMap(Object... entries) {
258+
Map<DocumentKey, T> res = new LinkedHashMap<>();
259+
for (int i = 0; i < entries.length; i += 2) {
260+
res.put(DocumentKey.fromPathString((String) entries[i]), (T) entries[i + 1]);
261+
}
262+
return res;
263+
}
264+
257265
public static FieldFilter filter(String key, String operator, Object value) {
258266
return FieldFilter.create(field(key), operatorFromString(operator), wrap(value));
259267
}

0 commit comments

Comments
 (0)