diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/BloomFilter.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/BloomFilter.java index 9eba0f3f07b..e51a89906c7 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/BloomFilter.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/BloomFilter.java @@ -32,21 +32,19 @@ public BloomFilter(@NonNull byte[] bitmap, int padding, int hashCount) { throw new NullPointerException("Bitmap cannot be null."); } if (padding < 0 || padding >= 8) { - throw new IllegalArgumentException("Invalid padding: " + padding); + throw new BloomFilterException("Invalid padding: " + padding); } if (hashCount < 0) { - throw new IllegalArgumentException("Invalid hash count: " + hashCount); + throw new BloomFilterException("Invalid hash count: " + hashCount); } if (bitmap.length > 0 && hashCount == 0) { // Only empty bloom filter can have 0 hash count. - throw new IllegalArgumentException("Invalid hash count: " + hashCount); + throw new BloomFilterException("Invalid hash count: " + hashCount); } - if (bitmap.length == 0) { + if (bitmap.length == 0 && padding != 0) { // Empty bloom filter should have 0 padding. - if (padding != 0) { - throw new IllegalArgumentException( - "Expected padding of 0 when bitmap length is 0, but got " + padding); - } + throw new BloomFilterException( + "Expected padding of 0 when bitmap length is 0, but got " + padding); } this.bitmap = bitmap; @@ -133,8 +131,8 @@ private int getBitIndex(long hash1, long hash2, int hashIndex) { /** * Calculate modulo, where the dividend and divisor are treated as unsigned 64-bit longs. * - *

The implementation is taken from Guava, + *

The implementation is taken from Guava, * simplified to our needs. * *

diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/BloomFilterException.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/BloomFilterException.java new file mode 100644 index 00000000000..429b501ed47 --- /dev/null +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/BloomFilterException.java @@ -0,0 +1,23 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.firebase.firestore.remote; + +import androidx.annotation.NonNull; + +public class BloomFilterException extends RuntimeException { + public BloomFilterException(@NonNull String detailMessage) { + super(detailMessage); + } +} diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/ExistenceFilter.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/ExistenceFilter.java index 7be1ca4744a..06d2d5f924e 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/ExistenceFilter.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/ExistenceFilter.java @@ -14,20 +14,34 @@ package com.google.firebase.firestore.remote; +import androidx.annotation.Nullable; +import com.google.firestore.v1.BloomFilter; + /** Simplest form of existence filter */ public final class ExistenceFilter { private final int count; + private BloomFilter unchangedNames; public ExistenceFilter(int count) { this.count = count; } + public ExistenceFilter(int count, @Nullable BloomFilter unchangedNames) { + this.count = count; + this.unchangedNames = unchangedNames; + } + public int getCount() { return count; } + @Nullable + public BloomFilter getUnchangedNames() { + return unchangedNames; + } + @Override public String toString() { - return "ExistenceFilter{count=" + count + '}'; + return "ExistenceFilter{count=" + count + ", unchangedNames=" + unchangedNames + '}'; } } diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java index 7f1f7781045..655842a73af 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java @@ -945,8 +945,8 @@ public WatchChange decodeWatchChange(ListenResponse protoChange) { break; case FILTER: com.google.firestore.v1.ExistenceFilter protoFilter = protoChange.getFilter(); - // TODO: implement existence filter parsing (see b/33076578) - ExistenceFilter filter = new ExistenceFilter(protoFilter.getCount()); + ExistenceFilter filter = + new ExistenceFilter(protoFilter.getCount(), protoFilter.getUnchangedNames()); int targetId = protoFilter.getTargetId(); watchChange = new ExistenceFilterWatchChange(targetId, filter); break; diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteStore.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteStore.java index 965ceee35da..1f5ad514b46 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteStore.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteStore.java @@ -28,6 +28,7 @@ import com.google.firebase.firestore.local.LocalStore; import com.google.firebase.firestore.local.QueryPurpose; import com.google.firebase.firestore.local.TargetData; +import com.google.firebase.firestore.model.DatabaseId; import com.google.firebase.firestore.model.DocumentKey; import com.google.firebase.firestore.model.SnapshotVersion; import com.google.firebase.firestore.model.mutation.MutationBatch; @@ -758,6 +759,11 @@ public TargetData getTargetDataForTarget(int targetId) { return this.listenTargets.get(targetId); } + @Override + public DatabaseId getDatabaseId() { + return this.datastore.getDatabaseInfo().getDatabaseId(); + } + public Task runCountQuery(Query query) { if (canUseNetwork()) { return datastore.runCountQuery(query); diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/WatchChangeAggregator.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/WatchChangeAggregator.java index 1086af504db..435cbca9e24 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/WatchChangeAggregator.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/remote/WatchChangeAggregator.java @@ -23,12 +23,14 @@ import com.google.firebase.firestore.core.Target; import com.google.firebase.firestore.local.QueryPurpose; import com.google.firebase.firestore.local.TargetData; +import com.google.firebase.firestore.model.DatabaseId; import com.google.firebase.firestore.model.DocumentKey; import com.google.firebase.firestore.model.MutableDocument; import com.google.firebase.firestore.model.SnapshotVersion; import com.google.firebase.firestore.remote.WatchChange.DocumentChange; import com.google.firebase.firestore.remote.WatchChange.ExistenceFilterWatchChange; import com.google.firebase.firestore.remote.WatchChange.WatchTargetChange; +import com.google.firebase.firestore.util.Logger; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -56,6 +58,9 @@ public interface TargetMetadataProvider { */ @Nullable TargetData getTargetDataForTarget(int targetId); + + /** Returns the database ID of the Firestore instance. */ + DatabaseId getDatabaseId(); } private final TargetMetadataProvider targetMetadataProvider; @@ -75,6 +80,9 @@ public interface TargetMetadataProvider { */ private Set pendingTargetResets = new HashSet<>(); + /** The log tag to use for this class. */ + private static final String LOG_TAG = "WatchChangeAggregator"; + public WatchChangeAggregator(TargetMetadataProvider targetMetadataProvider) { this.targetMetadataProvider = targetMetadataProvider; } @@ -196,17 +204,78 @@ public void handleExistenceFilter(ExistenceFilterWatchChange watchChange) { expectedCount == 1, "Single document existence filter with count: %d", expectedCount); } } else { - long currentSize = getCurrentDocumentCountForTarget(targetId); + int currentSize = getCurrentDocumentCountForTarget(targetId); if (currentSize != expectedCount) { - // Existence filter mismatch: We reset the mapping and raise a new snapshot with - // `isFromCache:true`. - resetTarget(targetId); - pendingTargetResets.add(targetId); + + // Apply bloom filter to identify and mark removed documents. + boolean bloomFilterApplied = this.applyBloomFilter(watchChange, currentSize); + + if (!bloomFilterApplied) { + // If bloom filter application fails, we reset the mapping and + // trigger re-run of the query. + resetTarget(targetId); + pendingTargetResets.add(targetId); + } } } } } + /** Returns whether a bloom filter removed the deleted documents successfully. */ + private boolean applyBloomFilter(ExistenceFilterWatchChange watchChange, int currentCount) { + int expectedCount = watchChange.getExistenceFilter().getCount(); + com.google.firestore.v1.BloomFilter unchangedNames = + watchChange.getExistenceFilter().getUnchangedNames(); + + if (unchangedNames == null || !unchangedNames.hasBits()) { + return false; + } + + byte[] bitmap = unchangedNames.getBits().getBitmap().toByteArray(); + BloomFilter bloomFilter; + + try { + bloomFilter = + new BloomFilter( + bitmap, unchangedNames.getBits().getPadding(), unchangedNames.getHashCount()); + } catch (BloomFilterException e) { + Logger.warn( + LOG_TAG, + "Decoding the base64 bloom filter in existence filter failed (" + + e.getMessage() + + "); ignoring the bloom filter and falling back to full re-query."); + return false; + } + + int removedDocumentCount = this.filterRemovedDocuments(bloomFilter, watchChange.getTargetId()); + + return expectedCount == (currentCount - removedDocumentCount); + } + + /** + * Filter out removed documents based on bloom filter membership result and return number of + * documents removed. + */ + private int filterRemovedDocuments(BloomFilter bloomFilter, int targetId) { + ImmutableSortedSet existingKeys = + targetMetadataProvider.getRemoteKeysForTarget(targetId); + int removalCount = 0; + for (DocumentKey key : existingKeys) { + DatabaseId databaseId = targetMetadataProvider.getDatabaseId(); + String documentPath = + "projects/" + + databaseId.getProjectId() + + "/databases/" + + databaseId.getDatabaseId() + + "/documents/" + + key.getPath().canonicalString(); + if (!bloomFilter.mightContain(documentPath)) { + this.removeDocumentFromTarget(targetId, key, /*updatedDocument=*/ null); + removalCount++; + } + } + return removalCount; + } /** * Converts the currently accumulated state into a remote event at the provided snapshot version. * Resets the accumulated changes before returning. diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/remote/BloomFilterTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/remote/BloomFilterTest.java index 0e851a0fc4f..c2ba39c4d7f 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/remote/BloomFilterTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/remote/BloomFilterTest.java @@ -73,55 +73,53 @@ public void constructorShouldThrowNPEOnNullBitmap() { } @Test - public void constructorShouldThrowIAEOnEmptyBloomFilterWithNonZeroPadding() { - IllegalArgumentException exception = - assertThrows(IllegalArgumentException.class, () -> new BloomFilter(new byte[0], 1, 0)); + public void constructorShouldThrowBFEOnEmptyBloomFilterWithNonZeroPadding() { + BloomFilterException exception = + assertThrows(BloomFilterException.class, () -> new BloomFilter(new byte[0], 1, 0)); assertThat(exception) .hasMessageThat() .contains("Expected padding of 0 when bitmap length is 0, but got 1"); } @Test - public void constructorShouldThrowIAEOnNonEmptyBloomFilterWithZeroHashCount() { - IllegalArgumentException zeroHashCountException = - assertThrows(IllegalArgumentException.class, () -> new BloomFilter(new byte[] {1}, 1, 0)); + public void constructorShouldThrowBFEOnNonEmptyBloomFilterWithZeroHashCount() { + BloomFilterException zeroHashCountException = + assertThrows(BloomFilterException.class, () -> new BloomFilter(new byte[] {1}, 1, 0)); assertThat(zeroHashCountException).hasMessageThat().contains("Invalid hash count: 0"); } @Test - public void constructorShouldThrowIAEOnNegativePadding() { + public void constructorShouldThrowBFEOnNegativePadding() { { - IllegalArgumentException emptyBloomFilterException = - assertThrows(IllegalArgumentException.class, () -> new BloomFilter(new byte[0], -1, 0)); + BloomFilterException emptyBloomFilterException = + assertThrows(BloomFilterException.class, () -> new BloomFilter(new byte[0], -1, 0)); assertThat(emptyBloomFilterException).hasMessageThat().contains("Invalid padding: -1"); } { - IllegalArgumentException nonEmptyBloomFilterException = - assertThrows( - IllegalArgumentException.class, () -> new BloomFilter(new byte[] {1}, -1, 1)); + BloomFilterException nonEmptyBloomFilterException = + assertThrows(BloomFilterException.class, () -> new BloomFilter(new byte[] {1}, -1, 1)); assertThat(nonEmptyBloomFilterException).hasMessageThat().contains("Invalid padding: -1"); } } @Test - public void constructorShouldThrowIAEOnNegativeHashCount() { + public void constructorShouldThrowBFEOnNegativeHashCount() { { - IllegalArgumentException emptyBloomFilterException = - assertThrows(IllegalArgumentException.class, () -> new BloomFilter(new byte[0], 0, -1)); + BloomFilterException emptyBloomFilterException = + assertThrows(BloomFilterException.class, () -> new BloomFilter(new byte[0], 0, -1)); assertThat(emptyBloomFilterException).hasMessageThat().contains("Invalid hash count: -1"); } { - IllegalArgumentException nonEmptyBloomFilterException = - assertThrows( - IllegalArgumentException.class, () -> new BloomFilter(new byte[] {1}, 1, -1)); + BloomFilterException nonEmptyBloomFilterException = + assertThrows(BloomFilterException.class, () -> new BloomFilter(new byte[] {1}, 1, -1)); assertThat(nonEmptyBloomFilterException).hasMessageThat().contains("Invalid hash count: -1"); } } @Test - public void constructorShouldThrowIAEIfPaddingIsTooLarge() { - IllegalArgumentException exception = - assertThrows(IllegalArgumentException.class, () -> new BloomFilter(new byte[] {1}, 8, 1)); + public void constructorShouldThrowBFEIfPaddingIsTooLarge() { + BloomFilterException exception = + assertThrows(BloomFilterException.class, () -> new BloomFilter(new byte[] {1}, 8, 1)); assertThat(exception).hasMessageThat().contains("Invalid padding: 8"); } @@ -179,7 +177,7 @@ public void bloomFilterToString() { private static void runGoldenTest(String testFile) throws Exception { String resultFile = testFile.replace("bloom_filter_proto", "membership_test_result"); if (resultFile.equals(testFile)) { - throw new IllegalArgumentException("Cannot find corresponding result file for " + testFile); + throw new BloomFilterException("Cannot find corresponding result file for " + testFile); } JSONObject testJson = readJsonFile(testFile); diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java index 7be9479da3b..42f570711b1 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java @@ -31,6 +31,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.util.Base64; import android.util.Pair; import androidx.test.core.app.ApplicationProvider; import com.google.android.gms.tasks.Task; @@ -81,6 +82,8 @@ import com.google.firebase.firestore.util.Assert; import com.google.firebase.firestore.util.AsyncQueue; import com.google.firebase.firestore.util.AsyncQueue.TimerId; +import com.google.firestore.v1.BitSequence; +import com.google.firestore.v1.BloomFilter; import com.google.protobuf.ByteString; import io.grpc.Status; import java.io.BufferedReader; @@ -468,6 +471,24 @@ private List parseIntList(@Nullable JSONArray arr) throws JSONException return result; } + /** Deeply parses a JSONObject into a bloom filter proto type. */ + private BloomFilter parseBloomFilter(JSONObject obj) throws JSONException { + BitSequence.Builder bitSequence = BitSequence.newBuilder(); + JSONObject bits = obj.getJSONObject("bits"); + if (bits.has("padding")) { + bitSequence.setPadding(bits.getInt("padding")); + } + bitSequence.setBitmap( + ByteString.copyFrom(Base64.decode(bits.getString("bitmap"), Base64.DEFAULT))); + + BloomFilter.Builder bloomFilter = BloomFilter.newBuilder(); + bloomFilter.setBits(bitSequence); + if (obj.has("hashCount")) { + bloomFilter.setHashCount(obj.getInt("hashCount")); + } + return bloomFilter.build(); + } + // // Methods for doing the steps of the spec test. // @@ -654,15 +675,19 @@ private void doWatchEntity(JSONObject watchEntity) throws Exception { } } - private void doWatchFilter(JSONArray watchFilter) throws Exception { - List targets = parseIntList(watchFilter.getJSONArray(0)); + private void doWatchFilter(JSONObject watchFilter) throws Exception { + List targets = parseIntList(watchFilter.getJSONArray("targetIds")); + Assert.hardAssert( targets.size() == 1, "ExistenceFilters currently support exactly one target only."); - int keyCount = watchFilter.length() == 0 ? 0 : watchFilter.length() - 1; + int keyCount = watchFilter.getJSONArray("keys").length(); + BloomFilter bloomFilterProto = + watchFilter.has("bloomFilter") + ? parseBloomFilter(watchFilter.getJSONObject("bloomFilter")) + : null; - // TODO: extend this with different existence filters over time. - ExistenceFilter filter = new ExistenceFilter(keyCount); + ExistenceFilter filter = new ExistenceFilter(keyCount, bloomFilterProto); ExistenceFilterWatchChange change = new ExistenceFilterWatchChange(targets.get(0), filter); writeWatchChange(change, SnapshotVersion.NONE); } @@ -713,7 +738,7 @@ private void doWriteAck(JSONObject writeAckSpec) throws Exception { validateNextWriteSent(write.first); MutationResult mutationResult = - new MutationResult(version(version), /*transformResults=*/ Collections.emptyList()); + new MutationResult(version(version), /* transformResults= */ Collections.emptyList()); queue.runSync(() -> datastore.ackWrite(version(version), singletonList(mutationResult))); } @@ -830,7 +855,7 @@ private void doStep(JSONObject step) throws Exception { } else if (step.has("watchEntity")) { doWatchEntity(step.getJSONObject("watchEntity")); } else if (step.has("watchFilter")) { - doWatchFilter(step.getJSONArray("watchFilter")); + doWatchFilter(step.getJSONObject("watchFilter")); } else if (step.has("watchReset")) { doWatchReset(step.getJSONArray("watchReset")); } else if (step.has("watchSnapshot")) { @@ -899,7 +924,17 @@ private void assertEventMatches(JSONObject expected, QueryEvent actual) throws J for (int i = 0; metadata != null && i < metadata.length(); ++i) { expectedChanges.add(parseChange(metadata.getJSONObject(i), Type.METADATA)); } - assertEquals(expectedChanges, actual.view.getChanges()); + + List sortedActualChanges = + actual.view.getChanges().stream() + .sorted((a, b) -> a.getDocument().getKey().compareTo(b.getDocument().getKey())) + .collect(Collectors.toList()); + List sortedExpectedChanges = + expectedChanges.stream() + .sorted((a, b) -> a.getDocument().getKey().compareTo(b.getDocument().getKey())) + .collect(Collectors.toList()); + + assertEquals(sortedExpectedChanges, sortedActualChanges); boolean expectedHasPendingWrites = expected.optBoolean("hasPendingWrites", false); boolean expectedFromCache = expected.optBoolean("fromCache", false); diff --git a/firebase-firestore/src/test/resources/json/existence_filter_spec_test.json b/firebase-firestore/src/test/resources/json/existence_filter_spec_test.json index 3083b762224..a66a08bce17 100644 --- a/firebase-firestore/src/test/resources/json/existence_filter_spec_test.json +++ b/firebase-firestore/src/test/resources/json/existence_filter_spec_test.json @@ -1,9 +1,8 @@ { - "Existence filter clears resume token": { + "Bloom filter can process special characters in document name": { "describeName": "Existence Filters:", - "itName": "Existence filter clears resume token", + "itName": "Bloom filter can process special characters in document name", "tags": [ - "durable-persistence" ], "config": { "numClients": 1, @@ -47,7 +46,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/ÀÒ∑", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -58,13 +57,13 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/À∑Ò", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, "version": 1000 } @@ -92,7 +91,7 @@ { "added": [ { - "key": "collection/1", + "key": "collection/ÀÒ∑", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -103,13 +102,13 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/À∑Ò", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, "version": 1000 } @@ -128,12 +127,21 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "IIAAIIAIIAAIIAIIAA==", + "padding": 4 + }, + "hashCount": 10 + }, + "keys": [ + "collection/ÀÒ∑" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -154,19 +162,51 @@ "path": "collection" } } - ] - }, - { - "restart": true, + ], "expectedState": { "activeLimboDocs": [ + "collection/À∑Ò" ], "activeTargets": { - }, - "enqueuedLimboDocs": [ - ] + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/À∑Ò" + } + ], + "resumeToken": "" + }, + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } } - }, + } + ] + }, + "Bloom filter fills in default values for undefined padding and hashCount": { + "describeName": "Existence Filters:", + "itName": "Bloom filter fills in default values for undefined padding and hashCount", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ { "userListen": { "query": { @@ -178,11 +218,78 @@ }, "targetId": 2 }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, "expectedSnapshotEvents": [ { "added": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -193,7 +300,7 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/b", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -204,6 +311,42 @@ "version": 1000 } ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "AhAAApAAAIAEBIAABA==" + } + }, + "keys": [ + "collection/a" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { "errorCode": 0, "fromCache": true, "hasPendingWrites": false, @@ -235,9 +378,9 @@ } ] }, - "Existence filter handled at global snapshot": { + "Bloom filter is handled at global snapshot": { "describeName": "Existence Filters:", - "itName": "Existence filter handled at global snapshot", + "itName": "Bloom filter is handled at global snapshot", "tags": [ ], "config": { @@ -282,7 +425,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -291,6 +434,17 @@ "v": 1 }, "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 } ], "targets": [ @@ -316,7 +470,7 @@ { "added": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -325,6 +479,17 @@ "v": 1 }, "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 } ], "errorCode": 0, @@ -341,19 +506,27 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "AhAAApAAAIAEBIAABA==", + "padding": 4 + }, + "hashCount": 10 + }, + "keys": [ + "collection/a" ], - "collection/1", - "collection/2" - ] + "targetIds": [ + 2 + ] + } }, { "watchEntity": { "docs": [ { - "key": "collection/3", + "key": "collection/c", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -379,7 +552,7 @@ { "added": [ { - "key": "collection/3", + "key": "collection/c", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -403,7 +576,22 @@ } ], "expectedState": { + "activeLimboDocs": [ + "collection/b" + ], "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/b" + } + ], + "resumeToken": "" + }, "2": { "queries": [ { @@ -418,12 +606,45 @@ } } } - }, + } + ] + }, + "Bloom filter limbo resolution is denied": { + "describeName": "Existence Filters:", + "itName": "Bloom filter limbo resolution is denied", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ { - "watchRemove": { - "targetIds": [ - 2 - ] + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } } }, { @@ -435,7 +656,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -446,26 +667,15 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/b", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, - "version": 2000 - }, - { - "key": "collection/3", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 3 - }, - "version": 3000 + "version": 1000 } ], "targets": [ @@ -478,135 +688,39 @@ [ 2 ], - "resume-token-3000" + "resume-token-1000" ] }, { "watchSnapshot": { "targetIds": [ ], - "version": 3000 + "version": 1000 }, "expectedSnapshotEvents": [ { "added": [ { - "key": "collection/2", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, - "version": 2000 - } - ], - "errorCode": 0, - "fromCache": false, - "hasPendingWrites": false, - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - } - ] - } - ] - }, - "Existence filter ignored with pending target": { - "describeName": "Existence Filters:", - "itName": "Existence filter ignored with pending target", - "tags": [ - ], - "config": { - "numClients": 1, - "useGarbageCollection": false - }, - "steps": [ - { - "userListen": { - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - }, - "targetId": 2 - }, - "expectedState": { - "activeTargets": { - "2": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - ], - "resumeToken": "" - } - } - } - }, - { - "watchAck": [ - 2 - ] - }, - { - "watchEntity": { - "docs": [ - { - "key": "collection/1", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 2 + "version": 1000 }, - "version": 2000 - } - ], - "targets": [ - 2 - ] - } - }, - { - "watchCurrent": [ - [ - 2 - ], - "resume-token-1000" - ] - }, - { - "watchSnapshot": { - "targetIds": [ - ], - "version": 1000 - }, - "expectedSnapshotEvents": [ - { - "added": [ { - "key": "collection/1", + "key": "collection/b", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, - "version": 2000 + "version": 1000 } ], "errorCode": 0, @@ -623,47 +737,30 @@ ] }, { - "userUnlisten": [ - 2, - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - ], - "expectedState": { - "activeTargets": { - } + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "AhAAApAAAIAEBIAABA==", + "padding": 4 + }, + "hashCount": 10 + }, + "keys": [ + "collection/a" + ], + "targetIds": [ + 2 + ] } }, { - "userListen": { - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - }, - "targetId": 2 + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 }, "expectedSnapshotEvents": [ { - "added": [ - { - "key": "collection/1", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 2 - }, - "version": 2000 - } - ], "errorCode": 0, "fromCache": true, "hasPendingWrites": false, @@ -677,7 +774,22 @@ } ], "expectedState": { + "activeLimboDocs": [ + "collection/b" + ], "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/b" + } + ], + "resumeToken": "" + }, "2": { "queries": [ { @@ -688,43 +800,19 @@ "path": "collection" } ], - "resumeToken": "resume-token-1000" + "resumeToken": "" } } } }, - { - "watchFilter": [ - [ - 2 - ] - ] - }, { "watchRemove": { + "cause": { + "code": 7 + }, "targetIds": [ - 2 + 1 ] - } - }, - { - "watchAck": [ - 2 - ] - }, - { - "watchCurrent": [ - [ - 2 - ], - "resume-token-2000" - ] - }, - { - "watchSnapshot": { - "targetIds": [ - ], - "version": 2000 }, "expectedSnapshotEvents": [ { @@ -737,15 +825,46 @@ "orderBys": [ ], "path": "collection" + }, + "removed": [ + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ] + } + ], + "expectedState": { + "activeLimboDocs": [ + ], + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" } } - ] + } } ] }, - "Existence filter limbo resolution is denied": { + "Bloom filter with large size works as expected": { "describeName": "Existence Filters:", - "itName": "Existence filter limbo resolution is denied", + "itName": "Bloom filter with large size works as expected", "tags": [ ], "config": { @@ -790,7 +909,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/doc0", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -801,27 +920,4388 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/doc1", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, "version": 1000 - } - ], - "targets": [ - 2 - ] - } - }, - { - "watchCurrent": [ - [ - 2 - ], + }, + { + "key": "collection/doc2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc4", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc5", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc6", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc7", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc8", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc9", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc10", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc11", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc12", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc13", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc14", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc15", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc16", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc17", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc18", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc19", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc20", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc21", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc22", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc23", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc24", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc25", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc26", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc27", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc28", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc29", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc30", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc31", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc32", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc33", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc34", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc35", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc36", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc37", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc38", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc39", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc40", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc41", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc42", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc43", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc44", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc45", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc46", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc47", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc48", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc49", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc50", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc51", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc52", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc53", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc54", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc55", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc56", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc57", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc58", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc59", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc60", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc61", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc62", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc63", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc64", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc65", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc66", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc67", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc68", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc69", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc70", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc71", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc72", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc73", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc74", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc75", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc76", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc77", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc78", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc79", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc80", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc81", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc82", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc83", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc84", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc85", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc86", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc87", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc88", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc89", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc90", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc91", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc92", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc93", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc94", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc95", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc96", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc97", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc98", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc99", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/doc0", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc4", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc5", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc6", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc7", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc8", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc9", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc10", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc11", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc12", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc13", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc14", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc15", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc16", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc17", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc18", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc19", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc20", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc21", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc22", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc23", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc24", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc25", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc26", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc27", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc28", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc29", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc30", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc31", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc32", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc33", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc34", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc35", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc36", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc37", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc38", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc39", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc40", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc41", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc42", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc43", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc44", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc45", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc46", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc47", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc48", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc49", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc50", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc51", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc52", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc53", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc54", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc55", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc56", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc57", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc58", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc59", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc60", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc61", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc62", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc63", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc64", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc65", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc66", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc67", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc68", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc69", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc70", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc71", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc72", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc73", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc74", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc75", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc76", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc77", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc78", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc79", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc80", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc81", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc82", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc83", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc84", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc85", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc86", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc87", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc88", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc89", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc90", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc91", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc92", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc93", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc94", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc95", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc96", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc97", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc98", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/doc99", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "+9oMQXUptl274DOaET8sfebQ4aCu0Roiddbja3z8TfadKuyPV/9XWV5Ksv+vywRXTfZSNIn8z+xk/oq1+cbOPepeNvbXVOF6H92fCOAz/KiS3Mcw338R9tXE3Y7QB1L2kbvbvVHW3Kn/k3Vx8k9Oa19eWX6RYE97Q+oCcVU=", + "padding": 0 + }, + "hashCount": 16 + }, + "keys": [ + "collection/doc0", + "collection/doc1", + "collection/doc2", + "collection/doc3", + "collection/doc4", + "collection/doc5", + "collection/doc6", + "collection/doc7", + "collection/doc8", + "collection/doc9", + "collection/doc10", + "collection/doc11", + "collection/doc12", + "collection/doc13", + "collection/doc14", + "collection/doc15", + "collection/doc16", + "collection/doc17", + "collection/doc18", + "collection/doc19", + "collection/doc20", + "collection/doc21", + "collection/doc22", + "collection/doc23", + "collection/doc24", + "collection/doc25", + "collection/doc26", + "collection/doc27", + "collection/doc28", + "collection/doc29", + "collection/doc30", + "collection/doc31", + "collection/doc32", + "collection/doc33", + "collection/doc34", + "collection/doc35", + "collection/doc36", + "collection/doc37", + "collection/doc38", + "collection/doc39", + "collection/doc40", + "collection/doc41", + "collection/doc42", + "collection/doc43", + "collection/doc44", + "collection/doc45", + "collection/doc46", + "collection/doc47", + "collection/doc48", + "collection/doc49" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeLimboDocs": [ + "collection/doc50", + "collection/doc51", + "collection/doc52", + "collection/doc53", + "collection/doc54", + "collection/doc55", + "collection/doc56", + "collection/doc57", + "collection/doc58", + "collection/doc59", + "collection/doc60", + "collection/doc61", + "collection/doc62", + "collection/doc63", + "collection/doc64", + "collection/doc65", + "collection/doc66", + "collection/doc67", + "collection/doc68", + "collection/doc69", + "collection/doc70", + "collection/doc71", + "collection/doc72", + "collection/doc73", + "collection/doc74", + "collection/doc75", + "collection/doc76", + "collection/doc77", + "collection/doc78", + "collection/doc79", + "collection/doc80", + "collection/doc81", + "collection/doc82", + "collection/doc83", + "collection/doc84", + "collection/doc85", + "collection/doc86", + "collection/doc87", + "collection/doc88", + "collection/doc89", + "collection/doc90", + "collection/doc91", + "collection/doc92", + "collection/doc93", + "collection/doc94", + "collection/doc95", + "collection/doc96", + "collection/doc97", + "collection/doc98", + "collection/doc99" + ], + "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc50" + } + ], + "resumeToken": "" + }, + "11": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc55" + } + ], + "resumeToken": "" + }, + "13": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc56" + } + ], + "resumeToken": "" + }, + "15": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc57" + } + ], + "resumeToken": "" + }, + "17": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc58" + } + ], + "resumeToken": "" + }, + "19": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc59" + } + ], + "resumeToken": "" + }, + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + }, + "21": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc60" + } + ], + "resumeToken": "" + }, + "23": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc61" + } + ], + "resumeToken": "" + }, + "25": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc62" + } + ], + "resumeToken": "" + }, + "27": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc63" + } + ], + "resumeToken": "" + }, + "29": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc64" + } + ], + "resumeToken": "" + }, + "3": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc51" + } + ], + "resumeToken": "" + }, + "31": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc65" + } + ], + "resumeToken": "" + }, + "33": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc66" + } + ], + "resumeToken": "" + }, + "35": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc67" + } + ], + "resumeToken": "" + }, + "37": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc68" + } + ], + "resumeToken": "" + }, + "39": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc69" + } + ], + "resumeToken": "" + }, + "41": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc70" + } + ], + "resumeToken": "" + }, + "43": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc71" + } + ], + "resumeToken": "" + }, + "45": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc72" + } + ], + "resumeToken": "" + }, + "47": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc73" + } + ], + "resumeToken": "" + }, + "49": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc74" + } + ], + "resumeToken": "" + }, + "5": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc52" + } + ], + "resumeToken": "" + }, + "51": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc75" + } + ], + "resumeToken": "" + }, + "53": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc76" + } + ], + "resumeToken": "" + }, + "55": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc77" + } + ], + "resumeToken": "" + }, + "57": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc78" + } + ], + "resumeToken": "" + }, + "59": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc79" + } + ], + "resumeToken": "" + }, + "61": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc80" + } + ], + "resumeToken": "" + }, + "63": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc81" + } + ], + "resumeToken": "" + }, + "65": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc82" + } + ], + "resumeToken": "" + }, + "67": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc83" + } + ], + "resumeToken": "" + }, + "69": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc84" + } + ], + "resumeToken": "" + }, + "7": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc53" + } + ], + "resumeToken": "" + }, + "71": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc85" + } + ], + "resumeToken": "" + }, + "73": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc86" + } + ], + "resumeToken": "" + }, + "75": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc87" + } + ], + "resumeToken": "" + }, + "77": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc88" + } + ], + "resumeToken": "" + }, + "79": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc89" + } + ], + "resumeToken": "" + }, + "81": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc90" + } + ], + "resumeToken": "" + }, + "83": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc91" + } + ], + "resumeToken": "" + }, + "85": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc92" + } + ], + "resumeToken": "" + }, + "87": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc93" + } + ], + "resumeToken": "" + }, + "89": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc94" + } + ], + "resumeToken": "" + }, + "9": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc54" + } + ], + "resumeToken": "" + }, + "91": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc95" + } + ], + "resumeToken": "" + }, + "93": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc96" + } + ], + "resumeToken": "" + }, + "95": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc97" + } + ], + "resumeToken": "" + }, + "97": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc98" + } + ], + "resumeToken": "" + }, + "99": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/doc99" + } + ], + "resumeToken": "" + } + } + } + } + ] + }, + "Existence filter clears resume token": { + "describeName": "Existence Filters:", + "itName": "Existence filter clears resume token", + "tags": [ + "durable-persistence" + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchFilter": { + "keys": [ + "collection/1" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "restart": true, + "expectedState": { + "activeLimboDocs": [ + ], + "activeTargets": { + }, + "enqueuedLimboDocs": [ + ] + } + }, + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + } + ] + }, + "Existence filter handled at global snapshot": { + "describeName": "Existence Filters:", + "itName": "Existence filter handled at global snapshot", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchFilter": { + "keys": [ + "collection/1", + "collection/2" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 3 + }, + "version": 3000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 3 + }, + "version": 3000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 + }, + { + "key": "collection/3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 3 + }, + "version": 3000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-3000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 3000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Existence filter ignored with pending target": { + "describeName": "Existence Filters:", + "itName": "Existence filter ignored with pending target", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": false + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "userUnlisten": [ + 2, + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "expectedState": { + "activeTargets": { + } + } + }, + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "resume-token-1000" + } + } + } + }, + { + "watchFilter": { + "keys": [ + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-2000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Existence filter limbo resolution is denied": { + "describeName": "Existence Filters:", + "itName": "Existence filter limbo resolution is denied", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchFilter": { + "keys": [ + "collection/1" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-2000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedState": { + "activeLimboDocs": [ + "collection/2" + ], + "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/2" + } + ], + "resumeToken": "" + }, + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchRemove": { + "cause": { + "code": 7 + }, + "targetIds": [ + 1 + ] + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "removed": [ + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ] + } + ], + "expectedState": { + "activeLimboDocs": [ + ], + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + } + ] + }, + "Existence filter match": { + "describeName": "Existence Filters:", + "itName": "Existence filter match", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchFilter": { + "keys": [ + "collection/1" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + } + } + ] + }, + "Existence filter match after pending update": { + "describeName": "Existence Filters:", + "itName": "Existence filter match after pending update", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchFilter": { + "keys": [ + "collection/1" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 2000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + } + ] + }, + "Existence filter mismatch triggers re-run of query": { + "describeName": "Existence Filters:", + "itName": "Existence filter mismatch triggers re-run of query", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], "resume-token-1000" ] }, @@ -829,30 +5309,676 @@ "watchSnapshot": { "targetIds": [ ], - "version": 1000 + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchFilter": { + "keys": [ + "collection/1" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-2000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedState": { + "activeLimboDocs": [ + "collection/2" + ], + "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/2" + } + ], + "resumeToken": "" + }, + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 1 + ] + }, + { + "watchCurrent": [ + [ + 1 + ], + "resume-token-2000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "removed": [ + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ] + } + ], + "expectedState": { + "activeLimboDocs": [ + ], + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + } + ] + }, + "Existence filter mismatch will drop resume token": { + "describeName": "Existence Filters:", + "itName": "Existence filter mismatch will drop resume token", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "existence-filter-resume-token" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchStreamClose": { + "error": { + "code": 14, + "message": "Simulated Backend Error" + }, + "runBackoffTimer": true + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "existence-filter-resume-token" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchFilter": { + "keys": [ + "collection/1" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-2000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedState": { + "activeLimboDocs": [ + "collection/2" + ], + "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/2" + } + ], + "resumeToken": "" + }, + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 1 + ] + }, + { + "watchCurrent": [ + [ + 1 + ], + "resume-token-2000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 }, "expectedSnapshotEvents": [ { - "added": [ + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "removed": [ { - "key": "collection/1", + "key": "collection/2", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 1 + "v": 2 }, "version": 1000 + } + ] + } + ], + "expectedState": { + "activeLimboDocs": [ + ], + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + } + ] + }, + "Existence filter synthesizes deletes": { + "describeName": "Existence Filters:", + "itName": "Existence filter synthesizes deletes", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/a" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/a" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ { - "key": "collection/2", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, "version": 1000 } @@ -865,18 +5991,19 @@ ], "orderBys": [ ], - "path": "collection" + "path": "collection/a" } } ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -887,17 +6014,54 @@ "expectedSnapshotEvents": [ { "errorCode": 0, - "fromCache": true, + "fromCache": false, "hasPendingWrites": false, "query": { "filters": [ ], "orderBys": [ ], - "path": "collection" - } + "path": "collection/a" + }, + "removed": [ + { + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ] } - ], + ] + } + ] + }, + "Existence filter with empty target": { + "describeName": "Existence Filters:", + "itName": "Existence filter with empty target", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, "expectedState": { "activeTargets": { "2": { @@ -915,44 +6079,17 @@ } } }, - { - "watchRemove": { - "targetIds": [ - 2 - ] - } - }, { "watchAck": [ 2 ] }, - { - "watchEntity": { - "docs": [ - { - "key": "collection/1", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 1 - }, - "version": 1000 - } - ], - "targets": [ - 2 - ] - } - }, { "watchCurrent": [ [ 2 ], - "resume-token-2000" + "resume-token-1000" ] }, { @@ -961,51 +6098,41 @@ ], "version": 2000 }, - "expectedState": { - "activeLimboDocs": [ - "collection/2" - ], - "activeTargets": { - "1": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection/2" - } + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ ], - "resumeToken": "" - }, - "2": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } + "orderBys": [ ], - "resumeToken": "" + "path": "collection" } } - } + ] }, { - "watchRemove": { - "cause": { - "code": 7 - }, + "watchFilter": { + "keys": [ + "collection/1" + ], "targetIds": [ - 1 + 2 ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 }, "expectedSnapshotEvents": [ { "errorCode": 0, - "fromCache": false, + "fromCache": true, "hasPendingWrites": false, "query": { "filters": [ @@ -1013,46 +6140,15 @@ "orderBys": [ ], "path": "collection" - }, - "removed": [ - { - "key": "collection/2", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 2 - }, - "version": 1000 - } - ] - } - ], - "expectedState": { - "activeLimboDocs": [ - ], - "activeTargets": { - "2": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - ], - "resumeToken": "" } } - } + ] } ] }, - "Existence filter match": { + "Full re-query is skipped when bloom filter can identify documents deleted": { "describeName": "Existence Filters:", - "itName": "Existence filter match", + "itName": "Full re-query is skipped when bloom filter can identify documents deleted", "tags": [ ], "config": { @@ -1097,7 +6193,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1106,6 +6202,17 @@ "v": 1 }, "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 } ], "targets": [ @@ -1131,7 +6238,7 @@ { "added": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1140,6 +6247,17 @@ "v": 1 }, "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 } ], "errorCode": 0, @@ -1156,11 +6274,85 @@ ] }, { - "watchFilter": [ - [ + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "AhAAApAAAIAEBIAABA==", + "padding": 4 + }, + "hashCount": 10 + }, + "keys": [ + "collection/a" + ], + "targetIds": [ 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeLimboDocs": [ + "collection/b" + ], + "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/b" + } + ], + "resumeToken": "" + }, + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 1 + ] + }, + { + "watchCurrent": [ + [ + 1 ], - "collection/1" + "resume-token-2000" ] }, { @@ -1168,14 +6360,61 @@ "targetIds": [ ], "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "removed": [ + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ] + } + ], + "expectedState": { + "activeLimboDocs": [ + ], + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } } } ] }, - "Existence filter match after pending update": { + "Full re-query is triggered when bloom filter bitmap is invalid": { "describeName": "Existence Filters:", - "itName": "Existence filter match after pending update", + "itName": "Full re-query is triggered when bloom filter bitmap is invalid", "tags": [ + "no-ios", + "no-android" ], "config": { "numClients": 1, @@ -1215,48 +6454,30 @@ 2 ] }, - { - "watchCurrent": [ - [ - 2 - ], - "resume-token-1000" - ] - }, - { - "watchSnapshot": { - "targetIds": [ - ], - "version": 2000 - }, - "expectedSnapshotEvents": [ - { - "errorCode": 0, - "fromCache": false, - "hasPendingWrites": false, - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - } - ] - }, { "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, - "version": 2000 + "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 } ], "targets": [ @@ -1265,32 +6486,43 @@ } }, { - "watchFilter": [ + "watchCurrent": [ [ 2 ], - "collection/1" + "resume-token-1000" ] }, { "watchSnapshot": { "targetIds": [ ], - "version": 2000 + "version": 1000 }, "expectedSnapshotEvents": [ { "added": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, - "version": 2000 + "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 } ], "errorCode": 0, @@ -1305,12 +6537,66 @@ } } ] + }, + { + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "INVALID_BASE_64", + "padding": 4 + }, + "hashCount": 10 + }, + "keys": [ + "collection/a" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } } ] }, - "Existence filter mismatch triggers re-run of query": { + "Full re-query is triggered when bloom filter can not identify documents deleted": { "describeName": "Existence Filters:", - "itName": "Existence filter mismatch triggers re-run of query", + "itName": "Full re-query is triggered when bloom filter can not identify documents deleted", "tags": [ ], "config": { @@ -1355,7 +6641,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1366,7 +6652,18 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + }, + { + "key": "collection/c", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1400,7 +6697,7 @@ { "added": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1411,7 +6708,18 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + }, + { + "key": "collection/c", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1436,12 +6744,21 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "AxBIApBIAIAWBoCQBA==", + "padding": 4 + }, + "hashCount": 10 + }, + "keys": [ + "collection/a" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -1462,7 +6779,14 @@ "path": "collection" } } - ], + ] + }, + { + "watchRemove": { + "targetIds": [ + 2 + ] + }, "expectedState": { "activeTargets": { "2": { @@ -1479,12 +6803,45 @@ } } } - }, + } + ] + }, + "Full re-query is triggered when bloom filter hashCount is invalid": { + "describeName": "Existence Filters:", + "itName": "Full re-query is triggered when bloom filter hashCount is invalid", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ { - "watchRemove": { - "targetIds": [ - 2 - ] + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } } }, { @@ -1496,7 +6853,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1505,80 +6862,64 @@ "v": 1 }, "version": 1000 - } - ], - "targets": [ - 2 - ] - } - }, - { - "watchCurrent": [ - [ - 2 - ], - "resume-token-2000" - ] - }, - { - "watchSnapshot": { - "targetIds": [ - ], - "version": 2000 - }, - "expectedState": { - "activeLimboDocs": [ - "collection/2" - ], - "activeTargets": { - "1": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection/2" - } - ], - "resumeToken": "" }, - "2": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - ], - "resumeToken": "" + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 } - } + ], + "targets": [ + 2 + ] } }, - { - "watchAck": [ - 1 - ] - }, { "watchCurrent": [ [ - 1 + 2 ], - "resume-token-2000" + "resume-token-1000" ] }, { "watchSnapshot": { "targetIds": [ ], - "version": 2000 + "version": 1000 }, "expectedSnapshotEvents": [ { + "added": [ + { + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + } + ], "errorCode": 0, "fromCache": false, "hasPendingWrites": false, @@ -1588,25 +6929,48 @@ "orderBys": [ ], "path": "collection" + } + } + ] + }, + { + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "AhAAApAAAIAEBIAABA==", + "padding": 4 }, - "removed": [ - { - "key": "collection/2", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 2 - }, - "version": 1000 - } - ] + "hashCount": -1 + }, + "keys": [ + "collection/a" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 2000 + }, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } } ], "expectedState": { - "activeLimboDocs": [ - ], "activeTargets": { "2": { "queries": [ @@ -1625,9 +6989,9 @@ } ] }, - "Existence filter mismatch will drop resume token": { + "Full re-query is triggered when bloom filter is empty": { "describeName": "Existence Filters:", - "itName": "Existence filter mismatch will drop resume token", + "itName": "Full re-query is triggered when bloom filter is empty", "tags": [ ], "config": { @@ -1672,7 +7036,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1683,13 +7047,13 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/b", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, "version": 1000 } @@ -1704,7 +7068,7 @@ [ 2 ], - "existence-filter-resume-token" + "resume-token-1000" ] }, { @@ -1717,7 +7081,7 @@ { "added": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1728,13 +7092,13 @@ "version": 1000 }, { - "key": "collection/2", + "key": "collection/b", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 2 + "v": 1 }, "version": 1000 } @@ -1753,42 +7117,21 @@ ] }, { - "watchStreamClose": { - "error": { - "code": 14, - "message": "Simulated Backend Error" + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "", + "padding": 0 + }, + "hashCount": 0 }, - "runBackoffTimer": true - }, - "expectedState": { - "activeTargets": { - "2": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - ], - "resumeToken": "existence-filter-resume-token" - } - } - } - }, - { - "watchAck": [ - 2 - ] - }, - { - "watchFilter": [ - [ - 2 + "keys": [ + "collection/a" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -1826,12 +7169,55 @@ } } } - }, + } + ] + }, + "Same documents can have different bloom filters": { + "describeName": "Existence Filters:", + "itName": "Same documents can have different bloom filters", + "tags": [ + ], + "config": { + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ { - "watchRemove": { - "targetIds": [ - 2 - ] + "userListen": { + "query": { + "filters": [ + [ + "v", + "<=", + 2 + ] + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + [ + "v", + "<=", + 2 + ] + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } } }, { @@ -1843,7 +7229,7 @@ "watchEntity": { "docs": [ { - "key": "collection/1", + "key": "collection/a", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1852,6 +7238,17 @@ "v": 1 }, "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 } ], "targets": [ @@ -1864,81 +7261,80 @@ [ 2 ], - "resume-token-2000" - ] - }, - { - "watchSnapshot": { - "targetIds": [ - ], - "version": 2000 - }, - "expectedState": { - "activeLimboDocs": [ - "collection/2" - ], - "activeTargets": { - "1": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection/2" - } - ], - "resumeToken": "" - }, - "2": { - "queries": [ - { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - } - ], - "resumeToken": "" - } - } - } - }, - { - "watchAck": [ - 1 - ] - }, - { - "watchCurrent": [ - [ - 1 - ], - "resume-token-2000" + "resume-token-1000" ] }, { "watchSnapshot": { "targetIds": [ ], - "version": 2000 + "version": 1000 }, "expectedSnapshotEvents": [ { + "added": [ + { + "key": "collection/a", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 1 + }, + "version": 1000 + }, + { + "key": "collection/b", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 2 + }, + "version": 1000 + } + ], "errorCode": 0, "fromCache": false, "hasPendingWrites": false, "query": { "filters": [ + [ + "v", + "<=", + 2 + ] ], "orderBys": [ ], "path": "collection" - }, - "removed": [ + } + } + ] + }, + { + "userListen": { + "query": { + "filters": [ + [ + "v", + ">=", + 2 + ] + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 4 + }, + "expectedSnapshotEvents": [ + { + "added": [ { - "key": "collection/2", + "key": "collection/b", "options": { "hasCommittedMutations": false, "hasLocalMutations": false @@ -1948,17 +7344,35 @@ }, "version": 1000 } - ] + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + [ + "v", + ">=", + 2 + ] + ], + "orderBys": [ + ], + "path": "collection" + } } ], "expectedState": { - "activeLimboDocs": [ - ], "activeTargets": { "2": { "queries": [ { "filters": [ + [ + "v", + "<=", + 2 + ] ], "orderBys": [ ], @@ -1966,43 +7380,20 @@ } ], "resumeToken": "" - } - } - } - } - ] - }, - "Existence filter synthesizes deletes": { - "describeName": "Existence Filters:", - "itName": "Existence filter synthesizes deletes", - "tags": [ - ], - "config": { - "numClients": 1, - "useGarbageCollection": true - }, - "steps": [ - { - "userListen": { - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection/a" - }, - "targetId": 2 - }, - "expectedState": { - "activeTargets": { - "2": { + }, + "4": { "queries": [ { "filters": [ + [ + "v", + ">=", + 2 + ] ], "orderBys": [ ], - "path": "collection/a" + "path": "collection" } ], "resumeToken": "" @@ -2012,54 +7403,65 @@ }, { "watchAck": [ - 2 + 4 ] }, { "watchEntity": { "docs": [ { - "key": "collection/a", + "key": "collection/b", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 1 + "v": 2 + }, + "version": 1000 + }, + { + "key": "collection/c", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "v": 3 }, "version": 1000 } ], "targets": [ - 2 + 4 ] } }, { "watchCurrent": [ [ - 2 + 4 ], - "resume-token-1000" + "resume-token-1001" ] }, { "watchSnapshot": { "targetIds": [ ], - "version": 1000 + "version": 1001 }, "expectedSnapshotEvents": [ { "added": [ { - "key": "collection/a", + "key": "collection/c", "options": { "hasCommittedMutations": false, "hasLocalMutations": false }, "value": { - "v": 1 + "v": 3 }, "version": 1000 } @@ -2069,20 +7471,35 @@ "hasPendingWrites": false, "query": { "filters": [ + [ + "v", + ">=", + 2 + ] ], "orderBys": [ ], - "path": "collection/a" + "path": "collection" } } ] }, { - "watchFilter": [ - [ + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "CQ==", + "padding": 3 + }, + "hashCount": 2 + }, + "keys": [ + "collection/b" + ], + "targetIds": [ 2 ] - ] + } }, { "watchSnapshot": { @@ -2093,60 +7510,65 @@ "expectedSnapshotEvents": [ { "errorCode": 0, - "fromCache": false, + "fromCache": true, "hasPendingWrites": false, "query": { "filters": [ + [ + "v", + "<=", + 2 + ] ], "orderBys": [ ], - "path": "collection/a" - }, - "removed": [ - { - "key": "collection/a", - "options": { - "hasCommittedMutations": false, - "hasLocalMutations": false - }, - "value": { - "v": 1 - }, - "version": 1000 - } - ] + "path": "collection" + } } - ] - } - ] - }, - "Existence filter with empty target": { - "describeName": "Existence Filters:", - "itName": "Existence filter with empty target", - "tags": [ - ], - "config": { - "numClients": 1, - "useGarbageCollection": true - }, - "steps": [ - { - "userListen": { - "query": { - "filters": [ - ], - "orderBys": [ - ], - "path": "collection" - }, - "targetId": 2 - }, + ], "expectedState": { + "activeLimboDocs": [ + "collection/a" + ], "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/a" + } + ], + "resumeToken": "" + }, "2": { "queries": [ { "filters": [ + [ + "v", + "<=", + 2 + ] + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + }, + "4": { + "queries": [ + { + "filters": [ + [ + "v", + ">=", + 2 + ] ], "orderBys": [ ], @@ -2159,67 +7581,113 @@ } }, { - "watchAck": [ - 2 - ] - }, - { - "watchCurrent": [ - [ - 2 + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "CA==", + "padding": 4 + }, + "hashCount": 1 + }, + "keys": [ + "collection/b" ], - "resume-token-1000" - ] + "targetIds": [ + 4 + ] + } }, { "watchSnapshot": { "targetIds": [ ], - "version": 2000 + "version": 3000 }, "expectedSnapshotEvents": [ { "errorCode": 0, - "fromCache": false, + "fromCache": true, "hasPendingWrites": false, "query": { "filters": [ + [ + "v", + ">=", + 2 + ] ], "orderBys": [ ], "path": "collection" } } - ] - }, - { - "watchFilter": [ - [ - 2 - ], - "collection/1" - ] - }, - { - "watchSnapshot": { - "targetIds": [ + ], + "expectedState": { + "activeLimboDocs": [ + "collection/a", + "collection/c" ], - "version": 2000 - }, - "expectedSnapshotEvents": [ - { - "errorCode": 0, - "fromCache": true, - "hasPendingWrites": false, - "query": { - "filters": [ + "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/a" + } ], - "orderBys": [ + "resumeToken": "" + }, + "2": { + "queries": [ + { + "filters": [ + [ + "v", + "<=", + 2 + ] + ], + "orderBys": [ + ], + "path": "collection" + } ], - "path": "collection" + "resumeToken": "" + }, + "3": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/c" + } + ], + "resumeToken": "" + }, + "4": { + "queries": [ + { + "filters": [ + [ + "v", + ">=", + 2 + ] + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" } } - ] + } } ] } diff --git a/firebase-firestore/src/test/resources/json/limbo_spec_test.json b/firebase-firestore/src/test/resources/json/limbo_spec_test.json index 0b6abe08a2b..7babd7364f5 100644 --- a/firebase-firestore/src/test/resources/json/limbo_spec_test.json +++ b/firebase-firestore/src/test/resources/json/limbo_spec_test.json @@ -3386,11 +3386,13 @@ ] }, { - "watchFilter": [ - [ + "watchFilter": { + "keys": [ + ], + "targetIds": [ 1 ] - ] + } }, { "watchCurrent": [ @@ -7794,9 +7796,9 @@ } ] }, - "Limbo resolution throttling with existence filter mismatch": { + "Limbo resolution throttling with bloom filter application": { "describeName": "Limbo Documents:", - "itName": "Limbo resolution throttling with existence filter mismatch", + "itName": "Limbo resolution throttling with bloom filter application", "tags": [ ], "config": { @@ -8036,15 +8038,397 @@ } }, { - "watchFilter": [ + "watchFilter": { + "bloomFilter": { + "bits": { + "bitmap": "yABCEAeZURNRGAkgAQ==", + "padding": 4 + }, + "hashCount": 10 + }, + "keys": [ + "collection/b1", + "collection/b2", + "collection/b3" + ], + "targetIds": [ + 2 + ] + } + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1001 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/b1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "b1" + }, + "version": 1000 + }, + { + "key": "collection/b2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "b2" + }, + "version": 1000 + }, + { + "key": "collection/b3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "b3" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1002" + ] + }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1002 + }, + "expectedState": { + "activeLimboDocs": [ + "collection/a1", + "collection/a2" + ], + "activeTargets": { + "1": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/a1" + } + ], + "resumeToken": "" + }, + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "resume-token-1000" + }, + "3": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection/a2" + } + ], + "resumeToken": "" + } + }, + "enqueuedLimboDocs": [ + "collection/a3" + ] + } + } + ] + }, + "Limbo resolution throttling with existence filter mismatch": { + "describeName": "Limbo Documents:", + "itName": "Limbo resolution throttling with existence filter mismatch", + "tags": [ + ], + "config": { + "maxConcurrentLimboResolutions": 2, + "numClients": 1, + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": { + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + }, + "targetId": 2 + }, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/a1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a1" + }, + "version": 1000 + }, + { + "key": "collection/a2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a2" + }, + "version": 1000 + }, + { + "key": "collection/a3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a3" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ [ 2 ], - "collection/b1", - "collection/b2", - "collection/b3" + "resume-token-1000" ] }, + { + "watchSnapshot": { + "targetIds": [ + ], + "version": 1000 + }, + "expectedSnapshotEvents": [ + { + "added": [ + { + "key": "collection/a1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a1" + }, + "version": 1000 + }, + { + "key": "collection/a2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a2" + }, + "version": 1000 + }, + { + "key": "collection/a3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "a3" + }, + "version": 1000 + } + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ] + }, + { + "enableNetwork": false, + "expectedSnapshotEvents": [ + { + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false, + "query": { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + } + ], + "expectedState": { + "activeLimboDocs": [ + ], + "activeTargets": { + }, + "enqueuedLimboDocs": [ + ] + } + }, + { + "enableNetwork": true, + "expectedState": { + "activeTargets": { + "2": { + "queries": [ + { + "filters": [ + ], + "orderBys": [ + ], + "path": "collection" + } + ], + "resumeToken": "resume-token-1000" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + { + "key": "collection/b1", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "b1" + }, + "version": 1000 + }, + { + "key": "collection/b2", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "b2" + }, + "version": 1000 + }, + { + "key": "collection/b3", + "options": { + "hasCommittedMutations": false, + "hasLocalMutations": false + }, + "value": { + "key": "b3" + }, + "version": 1000 + } + ], + "targets": [ + 2 + ] + } + }, + { + "watchFilter": { + "keys": [ + "collection/b1", + "collection/b2", + "collection/b3" + ], + "targetIds": [ + 2 + ] + } + }, { "watchSnapshot": { "targetIds": [ diff --git a/firebase-firestore/src/test/resources/json/limit_spec_test.json b/firebase-firestore/src/test/resources/json/limit_spec_test.json index 40e48205956..856e0505d91 100644 --- a/firebase-firestore/src/test/resources/json/limit_spec_test.json +++ b/firebase-firestore/src/test/resources/json/limit_spec_test.json @@ -5455,12 +5455,14 @@ } }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/b" ], - "collection/b" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { diff --git a/firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestTargetMetadataProvider.java b/firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestTargetMetadataProvider.java index 71cb84b9de8..21394a90269 100644 --- a/firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestTargetMetadataProvider.java +++ b/firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestTargetMetadataProvider.java @@ -16,6 +16,7 @@ import com.google.firebase.database.collection.ImmutableSortedSet; import com.google.firebase.firestore.local.TargetData; +import com.google.firebase.firestore.model.DatabaseId; import com.google.firebase.firestore.model.DocumentKey; import com.google.firebase.firestore.remote.WatchChangeAggregator; import java.util.HashMap; @@ -41,6 +42,11 @@ public TargetData getTargetDataForTarget(int targetId) { return queryData.get(targetId); } + @Override + public DatabaseId getDatabaseId() { + return DatabaseId.forProject("test-project"); + } + /** Sets or replaces the local state for the provided query data. */ public void setSyncedKeys(TargetData targetData, ImmutableSortedSet keys) { this.queryData.put(targetData.getTargetId(), targetData); diff --git a/firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestUtil.java b/firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestUtil.java index 55f846f9270..daf64c9b3e3 100644 --- a/firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestUtil.java +++ b/firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestUtil.java @@ -108,6 +108,8 @@ public class TestUtil { public static final long ARBITRARY_SEQUENCE_NUMBER = 2; + private static final DatabaseId TEST_PROJECT = DatabaseId.forProject("project"); + @SuppressWarnings("unchecked") public static Map map(Object... entries) { Map res = new LinkedHashMap<>(); @@ -140,8 +142,7 @@ public static FieldMask fieldMask(String... fields) { public static final Map EMPTY_MAP = new HashMap<>(); public static Value wrap(Object value) { - DatabaseId databaseId = DatabaseId.forProject("project"); - UserDataReader dataReader = new UserDataReader(databaseId); + UserDataReader dataReader = new UserDataReader(TEST_PROJECT); // HACK: We use parseQueryValue() since it accepts scalars as well as arrays / objects, and // our tests currently use wrap() pretty generically so we don't know the intent. return dataReader.parseQueryValue(value); @@ -488,6 +489,11 @@ public TargetData getTargetDataForTarget(int targetId) { ResourcePath collectionPath = docs.get(0).getKey().getCollectionPath(); return targetData(targetId, QueryPurpose.LISTEN, collectionPath.toString()); } + + @Override + public DatabaseId getDatabaseId() { + return TEST_PROJECT; + } }); SnapshotVersion version = SnapshotVersion.NONE; @@ -535,13 +541,18 @@ public TargetData getTargetDataForTarget(int targetId) { ? targetData(targetId, QueryPurpose.LISTEN, doc.getKey().toString()) : null; } + + @Override + public DatabaseId getDatabaseId() { + return TEST_PROJECT; + } }); aggregator.handleDocumentChange(change); return aggregator.createRemoteEvent(doc.getVersion()); } public static SetMutation setMutation(String path, Map values) { - UserDataReader dataReader = new UserDataReader(DatabaseId.forProject("project")); + UserDataReader dataReader = new UserDataReader(TEST_PROJECT); ParsedSetData parsed = dataReader.parseSetData(values); // The order of the transforms doesn't matter, but we sort them so tests can assume a particular @@ -574,7 +585,7 @@ private static PatchMutation patchMutationHelper( } } - UserDataReader dataReader = new UserDataReader(DatabaseId.forProject("project")); + UserDataReader dataReader = new UserDataReader(TEST_PROJECT); ParsedUpdateData parsed = dataReader.parseUpdateData(values); // `mergeMutation()` provides an update mask for the merged fields, whereas `patchMutation()`