Skip to content

Commit 7a65339

Browse files
committed
Merge remote-tracking branch 'origin/mila/BloomFilter' into BloomFilterComplexIntegrationTest
2 parents e0a3493 + 06479a0 commit 7a65339

13 files changed

+412
-320
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/core/SyncEngine.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ public void handleRejectedListen(int targetId, Status error) {
428428
new RemoteEvent(
429429
SnapshotVersion.NONE,
430430
/* targetChanges= */ Collections.emptyMap(),
431-
/* targetMismatches= */ Collections.emptySet(),
431+
/* targetMismatches= */ Collections.emptyMap(),
432432
documentUpdates,
433433
limboDocuments);
434434
handleRemoteEvent(event);

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ public ImmutableSortedMap<DocumentKey, Document> applyRemoteEvent(RemoteEvent re
425425
targetCache.addMatchingKeys(change.getAddedDocuments(), targetId);
426426

427427
TargetData newTargetData = oldTargetData.withSequenceNumber(sequenceNumber);
428-
if (remoteEvent.getTargetMismatches().contains(targetId)) {
428+
if (remoteEvent.getTargetMismatches().containsKey(targetId)) {
429429
newTargetData =
430430
newTargetData
431431
.withResumeToken(ByteString.EMPTY, SnapshotVersion.NONE)

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

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ public enum QueryPurpose {
2222
/** The query was used to refill a query after an existence filter mismatch. */
2323
EXISTENCE_FILTER_MISMATCH,
2424

25+
/**
26+
* The query target was used if the query is the result of a false positive in the bloom filter.
27+
*/
28+
EXISTENCE_FILTER_MISMATCH_BLOOM,
29+
2530
/** The query was used to resolve a limbo document. */
2631
LIMBO_RESOLUTION,
2732
}

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteEvent.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.firebase.firestore.remote;
1616

17+
import com.google.firebase.firestore.local.QueryPurpose;
1718
import com.google.firebase.firestore.model.DocumentKey;
1819
import com.google.firebase.firestore.model.MutableDocument;
1920
import com.google.firebase.firestore.model.SnapshotVersion;
@@ -27,14 +28,14 @@
2728
public final class RemoteEvent {
2829
private final SnapshotVersion snapshotVersion;
2930
private final Map<Integer, TargetChange> targetChanges;
30-
private final Set<Integer> targetMismatches;
31+
private final Map<Integer, QueryPurpose> targetMismatches;
3132
private final Map<DocumentKey, MutableDocument> documentUpdates;
3233
private final Set<DocumentKey> resolvedLimboDocuments;
3334

3435
public RemoteEvent(
3536
SnapshotVersion snapshotVersion,
3637
Map<Integer, TargetChange> targetChanges,
37-
Set<Integer> targetMismatches,
38+
Map<Integer, QueryPurpose> targetMismatches,
3839
Map<DocumentKey, MutableDocument> documentUpdates,
3940
Set<DocumentKey> resolvedLimboDocuments) {
4041
this.snapshotVersion = snapshotVersion;
@@ -55,10 +56,10 @@ public Map<Integer, TargetChange> getTargetChanges() {
5556
}
5657

5758
/**
58-
* Returns a set of targets that is known to be inconsistent. Listens for these targets should be
59-
* re-established without resume tokens.
59+
* Returns a map of targets that is known to be inconsistent, and the purpose for re-listening.
60+
* Listens for these targets should be re-established without resume tokens.
6061
*/
61-
public Set<Integer> getTargetMismatches() {
62+
public Map<Integer, QueryPurpose> getTargetMismatches() {
6263
return targetMismatches;
6364
}
6465

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java

+2
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,8 @@ private String encodeLabel(QueryPurpose purpose) {
471471
return null;
472472
case EXISTENCE_FILTER_MISMATCH:
473473
return "existence-filter-mismatch";
474+
case EXISTENCE_FILTER_MISMATCH_BLOOM:
475+
return "existence-filter-mismatch-bloom";
474476
case LIMBO_RESOLUTION:
475477
return "limbo-document";
476478
default:

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteStore.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,8 @@ private void raiseWatchSnapshot(SnapshotVersion snapshotVersion) {
547547

548548
// Re-establish listens for the targets that have been invalidated by existence filter
549549
// mismatches.
550-
for (int targetId : remoteEvent.getTargetMismatches()) {
550+
for (Map.Entry<Integer, QueryPurpose> entry : remoteEvent.getTargetMismatches().entrySet()) {
551+
int targetId = entry.getKey();
551552
TargetData targetData = this.listenTargets.get(targetId);
552553
// A watched target might have been removed already.
553554
if (targetData != null) {
@@ -569,7 +570,7 @@ private void raiseWatchSnapshot(SnapshotVersion snapshotVersion) {
569570
targetData.getTarget(),
570571
targetId,
571572
targetData.getSequenceNumber(),
572-
QueryPurpose.EXISTENCE_FILTER_MISMATCH);
573+
/*purpose=*/ entry.getValue());
573574
this.sendWatchRequest(requestTargetData);
574575
}
575576
}

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/WatchChangeAggregator.java

+37-13
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,21 @@ public interface TargetMetadataProvider {
7575
private Map<DocumentKey, Set<Integer>> pendingDocumentTargetMapping = new HashMap<>();
7676

7777
/**
78-
* A list of targets with existence filter mismatches. These targets are known to be inconsistent
78+
* A map of targets with existence filter mismatches. These targets are known to be inconsistent
7979
* and their listens needs to be re-established by RemoteStore.
8080
*/
81-
private Set<Integer> pendingTargetResets = new HashSet<>();
81+
private Map<Integer, QueryPurpose> pendingTargetResets = new HashMap<>();
8282

8383
/** The log tag to use for this class. */
8484
private static final String LOG_TAG = "WatchChangeAggregator";
8585

86+
/** The bloom filter application status while handling existence filter mismatch. */
87+
private enum BloomFilterApplicationStatus {
88+
SUCCESS,
89+
SKIPPED,
90+
FALSE_POSITIVE
91+
}
92+
8693
public WatchChangeAggregator(TargetMetadataProvider targetMetadataProvider) {
8794
this.targetMetadataProvider = targetMetadataProvider;
8895
}
@@ -208,31 +215,40 @@ public void handleExistenceFilter(ExistenceFilterWatchChange watchChange) {
208215
if (currentSize != expectedCount) {
209216

210217
// Apply bloom filter to identify and mark removed documents.
211-
boolean bloomFilterApplied = this.applyBloomFilter(watchChange, currentSize);
218+
BloomFilterApplicationStatus status = this.applyBloomFilter(watchChange, currentSize);
212219

213-
if (!bloomFilterApplied) {
220+
if (status != BloomFilterApplicationStatus.SUCCESS) {
214221
// If bloom filter application fails, we reset the mapping and
215222
// trigger re-run of the query.
216223
resetTarget(targetId);
217-
pendingTargetResets.add(targetId);
224+
225+
QueryPurpose purpose =
226+
status == BloomFilterApplicationStatus.FALSE_POSITIVE
227+
? QueryPurpose.EXISTENCE_FILTER_MISMATCH_BLOOM
228+
: QueryPurpose.EXISTENCE_FILTER_MISMATCH;
229+
230+
pendingTargetResets.put(targetId, purpose);
218231
}
219232

220233
WatchChangeAggregatorTestingHooks.notifyOnExistenceFilterMismatch(
221234
WatchChangeAggregatorTestingHooks.ExistenceFilterMismatchInfo.from(
222-
bloomFilterApplied, currentSize, watchChange.getExistenceFilter()));
235+
status == BloomFilterApplicationStatus.SUCCESS,
236+
currentSize,
237+
watchChange.getExistenceFilter()));
223238
}
224239
}
225240
}
226241
}
227242

228-
/** Returns whether a bloom filter removed the deleted documents successfully. */
229-
private boolean applyBloomFilter(ExistenceFilterWatchChange watchChange, int currentCount) {
243+
/** Apply bloom filter to remove the deleted documents, and return the application status. */
244+
private BloomFilterApplicationStatus applyBloomFilter(
245+
ExistenceFilterWatchChange watchChange, int currentCount) {
230246
int expectedCount = watchChange.getExistenceFilter().getCount();
231247
com.google.firestore.v1.BloomFilter unchangedNames =
232248
watchChange.getExistenceFilter().getUnchangedNames();
233249

234250
if (unchangedNames == null || !unchangedNames.hasBits()) {
235-
return false;
251+
return BloomFilterApplicationStatus.SKIPPED;
236252
}
237253

238254
byte[] bitmap = unchangedNames.getBits().getBitmap().toByteArray();
@@ -248,12 +264,20 @@ private boolean applyBloomFilter(ExistenceFilterWatchChange watchChange, int cur
248264
"Applying bloom filter failed: ("
249265
+ e.getMessage()
250266
+ "); ignoring the bloom filter and falling back to full re-query.");
251-
return false;
267+
return BloomFilterApplicationStatus.SKIPPED;
268+
}
269+
270+
if (bloomFilter.getBitCount() == 0) {
271+
return BloomFilterApplicationStatus.SKIPPED;
252272
}
253273

254274
int removedDocumentCount = this.filterRemovedDocuments(bloomFilter, watchChange.getTargetId());
255275

256-
return expectedCount == (currentCount - removedDocumentCount);
276+
if (expectedCount != (currentCount - removedDocumentCount)) {
277+
return BloomFilterApplicationStatus.FALSE_POSITIVE;
278+
}
279+
280+
return BloomFilterApplicationStatus.SUCCESS;
257281
}
258282

259283
/**
@@ -345,14 +369,14 @@ public RemoteEvent createRemoteEvent(SnapshotVersion snapshotVersion) {
345369
new RemoteEvent(
346370
snapshotVersion,
347371
Collections.unmodifiableMap(targetChanges),
348-
Collections.unmodifiableSet(pendingTargetResets),
372+
Collections.unmodifiableMap(pendingTargetResets),
349373
Collections.unmodifiableMap(pendingDocumentUpdates),
350374
Collections.unmodifiableSet(resolvedLimboDocuments));
351375

352376
// Re-initialize the current state to ensure that we do not modify the generated RemoteEvent.
353377
pendingDocumentUpdates = new HashMap<>();
354378
pendingDocumentTargetMapping = new HashMap<>();
355-
pendingTargetResets = new HashSet<>();
379+
pendingTargetResets = new HashMap<>();
356380

357381
return remoteEvent;
358382
}

firebase-firestore/src/test/resources/json/bundle_spec_test.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,7 @@
11661166
}
11671167
],
11681168
"resumeToken": "",
1169-
"targetPurpose": 2
1169+
"targetPurpose": 3
11701170
},
11711171
"2": {
11721172
"queries": [

0 commit comments

Comments
 (0)