Skip to content

Commit 2139f5e

Browse files
authored
Add expected count to target (#4574)
1 parent 82280ca commit 2139f5e

File tree

9 files changed

+739
-10
lines changed

9 files changed

+739
-10
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,14 @@ public int listen(Query query) {
203203
hardAssert(!queryViewsByQuery.containsKey(query), "We already listen to query: %s", query);
204204

205205
TargetData targetData = localStore.allocateTarget(query.toTarget());
206-
remoteStore.listen(targetData);
207206

208207
ViewSnapshot viewSnapshot =
209208
initializeViewAndComputeSnapshot(
210209
query, targetData.getTargetId(), targetData.getResumeToken());
211210
syncEngineListener.onViewSnapshots(Collections.singletonList(viewSnapshot));
212211

212+
remoteStore.listen(targetData);
213+
213214
return targetData.getTargetId();
214215
}
215216

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ TargetData decodeTargetData(com.google.firebase.firestore.proto.Target targetPro
260260
QueryPurpose.LISTEN,
261261
version,
262262
lastLimboFreeSnapshotVersion,
263-
resumeToken);
263+
resumeToken,
264+
null);
264265
}
265266

266267
public com.google.firestore.bundle.BundledQuery encodeBundledQuery(BundledQuery bundledQuery) {

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

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
import static com.google.firebase.firestore.util.Preconditions.checkNotNull;
1818

19+
import androidx.annotation.Nullable;
1920
import com.google.firebase.firestore.core.Target;
2021
import com.google.firebase.firestore.model.SnapshotVersion;
2122
import com.google.firebase.firestore.remote.WatchStream;
2223
import com.google.protobuf.ByteString;
24+
import java.util.Objects;
2325

2426
/** An immutable set of metadata that the store will need to keep track of for each target. */
2527
public final class TargetData {
@@ -30,6 +32,7 @@ public final class TargetData {
3032
private final SnapshotVersion snapshotVersion;
3133
private final SnapshotVersion lastLimboFreeSnapshotVersion;
3234
private final ByteString resumeToken;
35+
private final @Nullable Integer expectedCount;
3336

3437
/**
3538
* Creates a new TargetData with the given values.
@@ -45,6 +48,9 @@ public final class TargetData {
4548
* @param resumeToken An opaque, server-assigned token that allows watching a target to be resumed
4649
* after disconnecting without retransmitting all the data that matches the target. The resume
4750
* token essentially identifies a point in time from which the server should resume sending
51+
* @param expectedCount The number of documents that last matched the query at the resume token or
52+
* read time. Documents are counted only when making a listen request with resume token or
53+
* read time, otherwise, keep it null.
4854
*/
4955
TargetData(
5056
Target target,
@@ -53,14 +59,16 @@ public final class TargetData {
5359
QueryPurpose purpose,
5460
SnapshotVersion snapshotVersion,
5561
SnapshotVersion lastLimboFreeSnapshotVersion,
56-
ByteString resumeToken) {
62+
ByteString resumeToken,
63+
@Nullable Integer expectedCount) {
5764
this.target = checkNotNull(target);
5865
this.targetId = targetId;
5966
this.sequenceNumber = sequenceNumber;
6067
this.lastLimboFreeSnapshotVersion = lastLimboFreeSnapshotVersion;
6168
this.purpose = purpose;
6269
this.snapshotVersion = checkNotNull(snapshotVersion);
6370
this.resumeToken = checkNotNull(resumeToken);
71+
this.expectedCount = expectedCount;
6472
}
6573

6674
/** Convenience constructor for use when creating a TargetData for the first time. */
@@ -72,7 +80,8 @@ public TargetData(Target target, int targetId, long sequenceNumber, QueryPurpose
7280
purpose,
7381
SnapshotVersion.NONE,
7482
SnapshotVersion.NONE,
75-
WatchStream.EMPTY_RESUME_TOKEN);
83+
WatchStream.EMPTY_RESUME_TOKEN,
84+
null);
7685
}
7786

7887
/** Creates a new target data instance with an updated sequence number. */
@@ -84,7 +93,8 @@ public TargetData withSequenceNumber(long sequenceNumber) {
8493
purpose,
8594
snapshotVersion,
8695
lastLimboFreeSnapshotVersion,
87-
resumeToken);
96+
resumeToken,
97+
/* expectedCount= */ null);
8898
}
8999

90100
/** Creates a new target data instance with an updated resume token and snapshot version. */
@@ -96,7 +106,21 @@ public TargetData withResumeToken(ByteString resumeToken, SnapshotVersion snapsh
96106
purpose,
97107
snapshotVersion,
98108
lastLimboFreeSnapshotVersion,
99-
resumeToken);
109+
resumeToken,
110+
expectedCount);
111+
}
112+
113+
/** Creates a new target data instance with an updated expected count. */
114+
public TargetData withExpectedCount(@Nullable Integer expectedCount) {
115+
return new TargetData(
116+
target,
117+
targetId,
118+
sequenceNumber,
119+
purpose,
120+
snapshotVersion,
121+
lastLimboFreeSnapshotVersion,
122+
resumeToken,
123+
expectedCount);
100124
}
101125

102126
/** Creates a new target data instance with an updated last limbo free snapshot version number. */
@@ -108,7 +132,8 @@ public TargetData withLastLimboFreeSnapshotVersion(SnapshotVersion lastLimboFree
108132
purpose,
109133
snapshotVersion,
110134
lastLimboFreeSnapshotVersion,
111-
resumeToken);
135+
resumeToken,
136+
expectedCount);
112137
}
113138

114139
public Target getTarget() {
@@ -135,6 +160,11 @@ public ByteString getResumeToken() {
135160
return resumeToken;
136161
}
137162

163+
@Nullable
164+
public Integer getExpectedCount() {
165+
return expectedCount;
166+
}
167+
138168
/**
139169
* Returns the last snapshot version for which the associated view contained no limbo documents.
140170
*/
@@ -158,7 +188,8 @@ public boolean equals(Object o) {
158188
&& purpose.equals(targetData.purpose)
159189
&& snapshotVersion.equals(targetData.snapshotVersion)
160190
&& lastLimboFreeSnapshotVersion.equals(targetData.lastLimboFreeSnapshotVersion)
161-
&& resumeToken.equals(targetData.resumeToken);
191+
&& resumeToken.equals(targetData.resumeToken)
192+
&& Objects.equals(expectedCount, targetData.expectedCount);
162193
}
163194

164195
@Override
@@ -170,6 +201,7 @@ public int hashCode() {
170201
result = 31 * result + snapshotVersion.hashCode();
171202
result = 31 * result + lastLimboFreeSnapshotVersion.hashCode();
172203
result = 31 * result + resumeToken.hashCode();
204+
result = 31 * result + Objects.hashCode(expectedCount);
173205
return result;
174206
}
175207

@@ -190,6 +222,8 @@ public String toString() {
190222
+ lastLimboFreeSnapshotVersion
191223
+ ", resumeToken="
192224
+ resumeToken
225+
+ ", expectedCount="
226+
+ expectedCount
193227
+ '}';
194228
}
195229
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,13 @@ public Target encodeTarget(TargetData targetData) {
499499
builder.setResumeToken(targetData.getResumeToken());
500500
}
501501

502+
// TODO(Mila) Incorporate this into the if statement above.
503+
if (targetData.getExpectedCount() != null
504+
&& (!targetData.getResumeToken().isEmpty()
505+
|| targetData.getSnapshotVersion().compareTo(SnapshotVersion.NONE) > 0)) {
506+
builder.setExpectedCount(Int32Value.newBuilder().setValue(targetData.getExpectedCount()));
507+
}
508+
502509
return builder.build();
503510
}
504511

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ public void listen(TargetData targetData) {
369369

370370
private void sendWatchRequest(TargetData targetData) {
371371
watchChangeAggregator.recordPendingTargetRequest(targetData.getTargetId());
372+
if (!targetData.getResumeToken().isEmpty()
373+
|| targetData.getSnapshotVersion().compareTo(SnapshotVersion.NONE) > 0) {
374+
int expectedCount = this.getRemoteKeysForTarget(targetData.getTargetId()).size();
375+
targetData = targetData.withExpectedCount(expectedCount);
376+
}
377+
372378
watchStream.watchQuery(targetData);
373379
}
374380

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,8 @@ public void testEncodesTargetData() {
378378
QueryPurpose.LISTEN,
379379
snapshotVersion,
380380
limboFreeVersion,
381-
resumeToken);
381+
resumeToken,
382+
null);
382383

383384
// Let the RPC serializer test various permutations of query serialization.
384385
com.google.firestore.v1.Target.QueryTarget queryTarget =

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,8 @@ private TargetData newTargetData(Query query, int targetId, long version) {
331331
QueryPurpose.LISTEN,
332332
version(version),
333333
version(version),
334-
resumeToken(version));
334+
resumeToken(version),
335+
null);
335336
}
336337

337338
/** Adds the given query data to the targetCache under test, committing immediately. */

firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,9 @@ private void validateExpectedState(@Nullable JSONObject expectedState) throws JS
10061006
targetData.withResumeToken(
10071007
ByteString.EMPTY, version(queryDataJson.getInt("readTime")));
10081008
}
1009+
if (queryDataJson.has("expectedCount")) {
1010+
targetData = targetData.withExpectedCount(queryDataJson.getInt("expectedCount"));
1011+
}
10091012

10101013
expectedActiveTargets.get(targetId).add(targetData);
10111014
}
@@ -1144,6 +1147,10 @@ private void validateActiveTargets() {
11441147
expectedTarget.getResumeToken().toStringUtf8(),
11451148
actualTarget.getResumeToken().toStringUtf8());
11461149

1150+
if (expectedTarget.getExpectedCount() != null) {
1151+
assertEquals(expectedTarget.getExpectedCount(), actualTarget.getExpectedCount());
1152+
}
1153+
11471154
actualTargets.remove(expected.getKey());
11481155
}
11491156

0 commit comments

Comments
 (0)