Skip to content

Commit c7561e2

Browse files
committed
initial code
1 parent 9e889fe commit c7561e2

File tree

7 files changed

+73
-51
lines changed

7 files changed

+73
-51
lines changed

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

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

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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 6 additions & 5 deletions
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+
* A map of targets that is known to be inconsistent, and the purpose for re-listening. Listens
60+
* 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

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ 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 databaseId from the
127+
* Encodes the given document key as a fully qualified name. This includes the
128+
* databaseId from the
128129
* constructor and the key path.
129130
*/
130131
public String encodeKey(DocumentKey key) {
@@ -149,8 +150,10 @@ private String encodeQueryPath(ResourcePath path) {
149150
private ResourcePath decodeQueryPath(String name) {
150151
ResourcePath resource = decodeResourceName(name);
151152
if (resource.length() == 4) {
152-
// In v1beta1 queries for collections at the root did not have a trailing "/documents". In v1
153-
// all resource paths contain "/documents". Preserve the ability to read the v1 form for
153+
// In v1beta1 queries for collections at the root did not have a trailing
154+
// "/documents". In v1
155+
// all resource paths contain "/documents". Preserve the ability to read the v1
156+
// form for
154157
// compatibility with queries persisted in the local query cache.
155158
return ResourcePath.EMPTY;
156159
} else {
@@ -167,8 +170,10 @@ private String encodeResourceName(DatabaseId databaseId, ResourcePath path) {
167170
}
168171

169172
/**
170-
* Decodes a fully qualified resource name into a resource path and validates that there is a
171-
* project and database encoded in the path. There are no guarantees that a local path is also
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
172177
* encoded in this resource name.
173178
*/
174179
private ResourcePath decodeResourceName(String encoded) {
@@ -178,15 +183,19 @@ private ResourcePath decodeResourceName(String encoded) {
178183
return resource;
179184
}
180185

181-
/** Creates the prefix for a fully qualified resource path, without a local path on the end. */
186+
/**
187+
* Creates the prefix for a fully qualified resource path, without a local path
188+
* on the end.
189+
*/
182190
private static ResourcePath encodedDatabaseId(DatabaseId databaseId) {
183191
return ResourcePath.fromSegments(
184192
Arrays.asList(
185193
"projects", databaseId.getProjectId(), "databases", databaseId.getDatabaseId()));
186194
}
187195

188196
/**
189-
* Decodes a fully qualified resource name into a resource path and validates that there is a
197+
* Decodes a fully qualified resource name into a resource path and validates
198+
* that there is a
190199
* project and database encoded in the path along with a local path.
191200
*/
192201
private static ResourcePath extractLocalPathFromResourceName(ResourcePath resourceName) {
@@ -197,7 +206,10 @@ private static ResourcePath extractLocalPathFromResourceName(ResourcePath resour
197206
return resourceName.popFirst(5);
198207
}
199208

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

221233
public com.google.firestore.v1.Document encodeDocument(DocumentKey key, ObjectValue value) {
222-
com.google.firestore.v1.Document.Builder builder =
223-
com.google.firestore.v1.Document.newBuilder();
234+
com.google.firestore.v1.Document.Builder builder = com.google.firestore.v1.Document.newBuilder();
224235
builder.setName(encodeKey(key));
225236
builder.putAllFields(value.getFieldsMap());
226237
return builder.build();
@@ -289,10 +300,9 @@ public com.google.firestore.v1.Write encodeMutation(Mutation mutation) {
289300
}
290301

291302
public Mutation decodeMutation(com.google.firestore.v1.Write mutation) {
292-
Precondition precondition =
293-
mutation.hasCurrentDocument()
294-
? decodePrecondition(mutation.getCurrentDocument())
295-
: Precondition.NONE;
303+
Precondition precondition = mutation.hasCurrentDocument()
304+
? decodePrecondition(mutation.getCurrentDocument())
305+
: Precondition.NONE;
296306

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

330340
private com.google.firestore.v1.Precondition encodePrecondition(Precondition precondition) {
331341
hardAssert(!precondition.isNone(), "Can't serialize an empty precondition");
332-
com.google.firestore.v1.Precondition.Builder builder =
333-
com.google.firestore.v1.Precondition.newBuilder();
342+
com.google.firestore.v1.Precondition.Builder builder = com.google.firestore.v1.Precondition.newBuilder();
334343
if (precondition.getUpdateTime() != null) {
335344
return builder.setUpdateTime(encodeVersion(precondition.getUpdateTime())).build();
336345
} else if (precondition.getExists() != null) {
@@ -390,8 +399,7 @@ private DocumentTransform.FieldTransform encodeFieldTransform(FieldTransform fie
390399
.setRemoveAllFromArray(ArrayValue.newBuilder().addAllValues(remove.getElements()))
391400
.build();
392401
} else if (transform instanceof NumericIncrementTransformOperation) {
393-
NumericIncrementTransformOperation incrementOperation =
394-
(NumericIncrementTransformOperation) transform;
402+
NumericIncrementTransformOperation incrementOperation = (NumericIncrementTransformOperation) transform;
395403
return DocumentTransform.FieldTransform.newBuilder()
396404
.setFieldPath(fieldTransform.getFieldPath().canonicalString())
397405
.setIncrement(incrementOperation.getOperand())
@@ -405,8 +413,7 @@ private FieldTransform decodeFieldTransform(DocumentTransform.FieldTransform fie
405413
switch (fieldTransform.getTransformTypeCase()) {
406414
case SET_TO_SERVER_VALUE:
407415
hardAssert(
408-
fieldTransform.getSetToServerValue()
409-
== DocumentTransform.FieldTransform.ServerValue.REQUEST_TIME,
416+
fieldTransform.getSetToServerValue() == DocumentTransform.FieldTransform.ServerValue.REQUEST_TIME,
410417
"Unknown transform setToServerValue: %s",
411418
fieldTransform.getSetToServerValue());
412419
return new FieldTransform(
@@ -433,9 +440,12 @@ private FieldTransform decodeFieldTransform(DocumentTransform.FieldTransform fie
433440

434441
public MutationResult decodeMutationResult(
435442
com.google.firestore.v1.WriteResult proto, SnapshotVersion commitVersion) {
436-
// NOTE: Deletes don't have an updateTime but the commit timestamp from the containing
437-
// CommitResponse or WriteResponse indicates essentially that the delete happened no later than
438-
// that. For our purposes we don't care exactly when the delete happened so long as we can tell
443+
// NOTE: Deletes don't have an updateTime but the commit timestamp from the
444+
// containing
445+
// CommitResponse or WriteResponse indicates essentially that the delete
446+
// happened no later than
447+
// that. For our purposes we don't care exactly when the delete happened so long
448+
// as we can tell
439449
// when an update on the watch stream is at or later than that change.
440450
SnapshotVersion version = decodeVersion(proto.getUpdateTime());
441451
if (SnapshotVersion.NONE.equals(version)) {
@@ -454,7 +464,8 @@ public MutationResult decodeMutationResult(
454464

455465
@Nullable
456466
public Map<String, String> encodeListenRequestLabels(TargetData targetData) {
457-
@Nullable String value = encodeLabel(targetData.getPurpose());
467+
@Nullable
468+
String value = encodeLabel(targetData.getPurpose());
458469
if (value == null) {
459470
return null;
460471
}
@@ -471,6 +482,8 @@ private String encodeLabel(QueryPurpose purpose) {
471482
return null;
472483
case EXISTENCE_FILTER_MISMATCH:
473484
return "existence-filter-mismatch";
485+
case EXISTENCE_FILTER_MISMATCH_BLOOM:
486+
return "existence-filter-mismatch-bloom";
474487
case LIMBO_RESOLUTION:
475488
return "limbo-document";
476489
default:
@@ -492,7 +505,8 @@ public Target encodeTarget(TargetData targetData) {
492505

493506
if (targetData.getResumeToken().isEmpty()
494507
&& targetData.getSnapshotVersion().compareTo(SnapshotVersion.NONE) > 0) {
495-
// TODO(wuandy): Consider removing above check because it is most likely true. Right now, many
508+
// TODO(wuandy): Consider removing above check because it is most likely true.
509+
// Right now, many
496510
// tests depend on this behaviour though (leaving min() out of serialization).
497511
builder.setReadTime(encodeTimestamp(targetData.getSnapshotVersion().getTimestamp()));
498512
} else {
@@ -649,11 +663,11 @@ private StructuredQuery.Filter encodeFilters(List<Filter> filters) {
649663
private List<Filter> decodeFilters(StructuredQuery.Filter proto) {
650664
Filter result = decodeFilter(proto);
651665

652-
// Instead of a singletonList containing AND(F1, F2, ...), we can return a list containing F1,
666+
// Instead of a singletonList containing AND(F1, F2, ...), we can return a list
667+
// containing F1,
653668
// F2, ... to stay consistent with the older SDK versions.
654669
if (result instanceof com.google.firebase.firestore.core.CompositeFilter) {
655-
com.google.firebase.firestore.core.CompositeFilter compositeFilter =
656-
(com.google.firebase.firestore.core.CompositeFilter) result;
670+
com.google.firebase.firestore.core.CompositeFilter compositeFilter = (com.google.firebase.firestore.core.CompositeFilter) result;
657671
if (compositeFilter.isFlatConjunction()) {
658672
return compositeFilter.getFilters();
659673
}
@@ -911,9 +925,8 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
911925
default:
912926
throw new IllegalArgumentException("Unknown target change type");
913927
}
914-
watchChange =
915-
new WatchTargetChange(
916-
changeType, targetChange.getTargetIdsList(), targetChange.getResumeToken(), cause);
928+
watchChange = new WatchTargetChange(
929+
changeType, targetChange.getTargetIdsList(), targetChange.getResumeToken(), cause);
917930
break;
918931
case DOCUMENT_CHANGE:
919932
DocumentChange docChange = protoChange.getDocumentChange();
@@ -934,8 +947,7 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
934947
// Note that version might be unset in which case we use SnapshotVersion.NONE
935948
version = decodeVersion(docDelete.getReadTime());
936949
MutableDocument doc = MutableDocument.newNoDocument(key, version);
937-
watchChange =
938-
new WatchChange.DocumentChange(Collections.emptyList(), removed, doc.getKey(), doc);
950+
watchChange = new WatchChange.DocumentChange(Collections.emptyList(), removed, doc.getKey(), doc);
939951
break;
940952
case DOCUMENT_REMOVE:
941953
DocumentRemove docRemove = protoChange.getDocumentRemove();
@@ -945,8 +957,7 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
945957
break;
946958
case FILTER:
947959
com.google.firestore.v1.ExistenceFilter protoFilter = protoChange.getFilter();
948-
ExistenceFilter filter =
949-
new ExistenceFilter(protoFilter.getCount(), protoFilter.getUnchangedNames());
960+
ExistenceFilter filter = new ExistenceFilter(protoFilter.getCount(), protoFilter.getUnchangedNames());
950961
int targetId = protoFilter.getTargetId();
951962
watchChange = new ExistenceFilterWatchChange(targetId, filter);
952963
break;
@@ -959,8 +970,10 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) {
959970
}
960971

961972
public SnapshotVersion decodeVersionFromListenResponse(ListenResponse watchChange) {
962-
// We have only reached a consistent snapshot for the entire stream if there is a read_time set
963-
// and it applies to all targets (i.e. the list of targets is empty). The backend is guaranteed
973+
// We have only reached a consistent snapshot for the entire stream if there is
974+
// a read_time set
975+
// and it applies to all targets (i.e. the list of targets is empty). The
976+
// backend is guaranteed
964977
// to send such responses.
965978
if (watchChange.getResponseTypeCase() != ResponseTypeCase.TARGET_CHANGE) {
966979
return SnapshotVersion.NONE;

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,10 @@ 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+
552+
int targetId = entry.getKey();
553+
551554
TargetData targetData = this.listenTargets.get(targetId);
552555
// A watched target might have been removed already.
553556
if (targetData != null) {
@@ -569,7 +572,7 @@ private void raiseWatchSnapshot(SnapshotVersion snapshotVersion) {
569572
targetData.getTarget(),
570573
targetId,
571574
targetData.getSequenceNumber(),
572-
QueryPurpose.EXISTENCE_FILTER_MISMATCH);
575+
/*QueryPurpose=*/ entry.getValue());
573576
this.sendWatchRequest(requestTargetData);
574577
}
575578
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ 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";
@@ -214,7 +214,7 @@ public void handleExistenceFilter(ExistenceFilterWatchChange watchChange) {
214214
// If bloom filter application fails, we reset the mapping and
215215
// trigger re-run of the query.
216216
resetTarget(targetId);
217-
pendingTargetResets.add(targetId);
217+
pendingTargetResets.put(targetId, QueryPurpose.EXISTENCE_FILTER_MISMATCH);
218218
}
219219
}
220220
}
@@ -341,14 +341,14 @@ public RemoteEvent createRemoteEvent(SnapshotVersion snapshotVersion) {
341341
new RemoteEvent(
342342
snapshotVersion,
343343
Collections.unmodifiableMap(targetChanges),
344-
Collections.unmodifiableSet(pendingTargetResets),
344+
Collections.unmodifiableMap(pendingTargetResets),
345345
Collections.unmodifiableMap(pendingDocumentUpdates),
346346
Collections.unmodifiableSet(resolvedLimboDocuments));
347347

348348
// Re-initialize the current state to ensure that we do not modify the generated RemoteEvent.
349349
pendingDocumentUpdates = new HashMap<>();
350350
pendingDocumentTargetMapping = new HashMap<>();
351-
pendingTargetResets = new HashSet<>();
351+
pendingTargetResets = new HashMap<>();
352352

353353
return remoteEvent;
354354
}

0 commit comments

Comments
 (0)