Skip to content

Commit 1b16c7a

Browse files
committed
set bloom filter application status
1 parent c7561e2 commit 1b16c7a

File tree

8 files changed

+563
-336
lines changed

8 files changed

+563
-336
lines changed

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

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ public SnapshotVersion decodeVersion(com.google.protobuf.Timestamp proto) {
124124
// Names and Keys
125125

126126
/**
127-
* Encodes the given document key as a fully qualified name. This includes the
128-
* databaseId from the
127+
* Encodes the given document key as a fully qualified name. This includes the databaseId from the
129128
* constructor and the key path.
130129
*/
131130
public String encodeKey(DocumentKey key) {
@@ -170,10 +169,8 @@ private String encodeResourceName(DatabaseId databaseId, ResourcePath path) {
170169
}
171170

172171
/**
173-
* Decodes a fully qualified resource name into a resource path and validates
174-
* that there is a
175-
* project and database encoded in the path. There are no guarantees that a
176-
* local path is also
172+
* Decodes a fully qualified resource name into a resource path and validates that there is a
173+
* project and database encoded in the path. There are no guarantees that a local path is also
177174
* encoded in this resource name.
178175
*/
179176
private ResourcePath decodeResourceName(String encoded) {
@@ -183,19 +180,15 @@ private ResourcePath decodeResourceName(String encoded) {
183180
return resource;
184181
}
185182

186-
/**
187-
* Creates the prefix for a fully qualified resource path, without a local path
188-
* on the end.
189-
*/
183+
/** Creates the prefix for a fully qualified resource path, without a local path on the end. */
190184
private static ResourcePath encodedDatabaseId(DatabaseId databaseId) {
191185
return ResourcePath.fromSegments(
192186
Arrays.asList(
193187
"projects", databaseId.getProjectId(), "databases", databaseId.getDatabaseId()));
194188
}
195189

196190
/**
197-
* Decodes a fully qualified resource name into a resource path and validates
198-
* that there is a
191+
* Decodes a fully qualified resource name into a resource path and validates that there is a
199192
* project and database encoded in the path along with a local path.
200193
*/
201194
private static ResourcePath extractLocalPathFromResourceName(ResourcePath resourceName) {
@@ -206,10 +199,7 @@ private static ResourcePath extractLocalPathFromResourceName(ResourcePath resour
206199
return resourceName.popFirst(5);
207200
}
208201

209-
/**
210-
* Validates that a path has a prefix that looks like a valid encoded
211-
* databaseId.
212-
*/
202+
/** Validates that a path has a prefix that looks like a valid encoded databaseId. */
213203
private static boolean isValidResourceName(ResourcePath path) {
214204
// Resource names have at least 4 components (project ID, database ID)
215205
// and commonly the (root) resource type, e.g. documents
@@ -231,7 +221,8 @@ public String databaseName() {
231221
// Documents
232222

233223
public com.google.firestore.v1.Document encodeDocument(DocumentKey key, ObjectValue value) {
234-
com.google.firestore.v1.Document.Builder builder = com.google.firestore.v1.Document.newBuilder();
224+
com.google.firestore.v1.Document.Builder builder =
225+
com.google.firestore.v1.Document.newBuilder();
235226
builder.setName(encodeKey(key));
236227
builder.putAllFields(value.getFieldsMap());
237228
return builder.build();
@@ -300,9 +291,10 @@ public com.google.firestore.v1.Write encodeMutation(Mutation mutation) {
300291
}
301292

302293
public Mutation decodeMutation(com.google.firestore.v1.Write mutation) {
303-
Precondition precondition = mutation.hasCurrentDocument()
304-
? decodePrecondition(mutation.getCurrentDocument())
305-
: Precondition.NONE;
294+
Precondition precondition =
295+
mutation.hasCurrentDocument()
296+
? decodePrecondition(mutation.getCurrentDocument())
297+
: Precondition.NONE;
306298

307299
List<FieldTransform> fieldTransforms = new ArrayList<>();
308300
for (DocumentTransform.FieldTransform fieldTransform : mutation.getUpdateTransformsList()) {
@@ -339,7 +331,8 @@ public Mutation decodeMutation(com.google.firestore.v1.Write mutation) {
339331

340332
private com.google.firestore.v1.Precondition encodePrecondition(Precondition precondition) {
341333
hardAssert(!precondition.isNone(), "Can't serialize an empty precondition");
342-
com.google.firestore.v1.Precondition.Builder builder = com.google.firestore.v1.Precondition.newBuilder();
334+
com.google.firestore.v1.Precondition.Builder builder =
335+
com.google.firestore.v1.Precondition.newBuilder();
343336
if (precondition.getUpdateTime() != null) {
344337
return builder.setUpdateTime(encodeVersion(precondition.getUpdateTime())).build();
345338
} else if (precondition.getExists() != null) {
@@ -399,7 +392,8 @@ private DocumentTransform.FieldTransform encodeFieldTransform(FieldTransform fie
399392
.setRemoveAllFromArray(ArrayValue.newBuilder().addAllValues(remove.getElements()))
400393
.build();
401394
} else if (transform instanceof NumericIncrementTransformOperation) {
402-
NumericIncrementTransformOperation incrementOperation = (NumericIncrementTransformOperation) transform;
395+
NumericIncrementTransformOperation incrementOperation =
396+
(NumericIncrementTransformOperation) transform;
403397
return DocumentTransform.FieldTransform.newBuilder()
404398
.setFieldPath(fieldTransform.getFieldPath().canonicalString())
405399
.setIncrement(incrementOperation.getOperand())
@@ -413,7 +407,8 @@ private FieldTransform decodeFieldTransform(DocumentTransform.FieldTransform fie
413407
switch (fieldTransform.getTransformTypeCase()) {
414408
case SET_TO_SERVER_VALUE:
415409
hardAssert(
416-
fieldTransform.getSetToServerValue() == DocumentTransform.FieldTransform.ServerValue.REQUEST_TIME,
410+
fieldTransform.getSetToServerValue()
411+
== DocumentTransform.FieldTransform.ServerValue.REQUEST_TIME,
417412
"Unknown transform setToServerValue: %s",
418413
fieldTransform.getSetToServerValue());
419414
return new FieldTransform(
@@ -464,8 +459,7 @@ public MutationResult decodeMutationResult(
464459

465460
@Nullable
466461
public Map<String, String> encodeListenRequestLabels(TargetData targetData) {
467-
@Nullable
468-
String value = encodeLabel(targetData.getPurpose());
462+
@Nullable String value = encodeLabel(targetData.getPurpose());
469463
if (value == null) {
470464
return null;
471465
}
@@ -667,7 +661,8 @@ private List<Filter> decodeFilters(StructuredQuery.Filter proto) {
667661
// containing F1,
668662
// F2, ... to stay consistent with the older SDK versions.
669663
if (result instanceof com.google.firebase.firestore.core.CompositeFilter) {
670-
com.google.firebase.firestore.core.CompositeFilter compositeFilter = (com.google.firebase.firestore.core.CompositeFilter) result;
664+
com.google.firebase.firestore.core.CompositeFilter compositeFilter =
665+
(com.google.firebase.firestore.core.CompositeFilter) result;
671666
if (compositeFilter.isFlatConjunction()) {
672667
return compositeFilter.getFilters();
673668
}
@@ -925,8 +920,9 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
925920
default:
926921
throw new IllegalArgumentException("Unknown target change type");
927922
}
928-
watchChange = new WatchTargetChange(
929-
changeType, targetChange.getTargetIdsList(), targetChange.getResumeToken(), cause);
923+
watchChange =
924+
new WatchTargetChange(
925+
changeType, targetChange.getTargetIdsList(), targetChange.getResumeToken(), cause);
930926
break;
931927
case DOCUMENT_CHANGE:
932928
DocumentChange docChange = protoChange.getDocumentChange();
@@ -947,7 +943,8 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
947943
// Note that version might be unset in which case we use SnapshotVersion.NONE
948944
version = decodeVersion(docDelete.getReadTime());
949945
MutableDocument doc = MutableDocument.newNoDocument(key, version);
950-
watchChange = new WatchChange.DocumentChange(Collections.emptyList(), removed, doc.getKey(), doc);
946+
watchChange =
947+
new WatchChange.DocumentChange(Collections.emptyList(), removed, doc.getKey(), doc);
951948
break;
952949
case DOCUMENT_REMOVE:
953950
DocumentRemove docRemove = protoChange.getDocumentRemove();
@@ -957,7 +954,8 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
957954
break;
958955
case FILTER:
959956
com.google.firestore.v1.ExistenceFilter protoFilter = protoChange.getFilter();
960-
ExistenceFilter filter = new ExistenceFilter(protoFilter.getCount(), protoFilter.getUnchangedNames());
957+
ExistenceFilter filter =
958+
new ExistenceFilter(protoFilter.getCount(), protoFilter.getUnchangedNames());
961959
int targetId = protoFilter.getTargetId();
962960
watchChange = new ExistenceFilterWatchChange(targetId, filter);
963961
break;

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

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ public interface TargetMetadataProvider {
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,27 +215,34 @@ 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.put(targetId, QueryPurpose.EXISTENCE_FILTER_MISMATCH);
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
}
221234
}
222235
}
223236

224-
/** Returns whether a bloom filter removed the deleted documents successfully. */
225-
private boolean applyBloomFilter(ExistenceFilterWatchChange watchChange, int currentCount) {
237+
/** Apply bloom filter to remove the deleted documents, and return the application status. */
238+
private BloomFilterApplicationStatus applyBloomFilter(
239+
ExistenceFilterWatchChange watchChange, int currentCount) {
226240
int expectedCount = watchChange.getExistenceFilter().getCount();
227241
com.google.firestore.v1.BloomFilter unchangedNames =
228242
watchChange.getExistenceFilter().getUnchangedNames();
229243

230244
if (unchangedNames == null || !unchangedNames.hasBits()) {
231-
return false;
245+
return BloomFilterApplicationStatus.SKIPPED;
232246
}
233247

234248
byte[] bitmap = unchangedNames.getBits().getBitmap().toByteArray();
@@ -244,12 +258,20 @@ private boolean applyBloomFilter(ExistenceFilterWatchChange watchChange, int cur
244258
"Decoding the base64 bloom filter in existence filter failed ("
245259
+ e.getMessage()
246260
+ "); ignoring the bloom filter and falling back to full re-query.");
247-
return false;
261+
return BloomFilterApplicationStatus.SKIPPED;
262+
}
263+
264+
if (bloomFilter.getBitCount() == 0) {
265+
return BloomFilterApplicationStatus.SKIPPED;
248266
}
249267

250268
int removedDocumentCount = this.filterRemovedDocuments(bloomFilter, watchChange.getTargetId());
251269

252-
return expectedCount == (currentCount - removedDocumentCount);
270+
if (expectedCount != (currentCount - removedDocumentCount)) {
271+
return BloomFilterApplicationStatus.FALSE_POSITIVE;
272+
}
273+
274+
return BloomFilterApplicationStatus.SUCCESS;
253275
}
254276

255277
/**

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

Lines changed: 1 addition & 1 deletion
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)