@@ -75,14 +75,21 @@ public interface TargetMetadataProvider {
75
75
private Map <DocumentKey , Set <Integer >> pendingDocumentTargetMapping = new HashMap <>();
76
76
77
77
/**
78
- * A list of targets with existence filter mismatches. These targets are known to be inconsistent
78
+ * A map of targets with existence filter mismatches. These targets are known to be inconsistent
79
79
* and their listens needs to be re-established by RemoteStore.
80
80
*/
81
- private Set <Integer > pendingTargetResets = new HashSet <>();
81
+ private Map <Integer , QueryPurpose > pendingTargetResets = new HashMap <>();
82
82
83
83
/** The log tag to use for this class. */
84
84
private static final String LOG_TAG = "WatchChangeAggregator" ;
85
85
86
+ /** The bloom filter application status while handling existence filter mismatch. */
87
+ private enum BloomFilterApplicationStatus {
88
+ SUCCESS ,
89
+ SKIPPED ,
90
+ FALSE_POSITIVE
91
+ }
92
+
86
93
public WatchChangeAggregator (TargetMetadataProvider targetMetadataProvider ) {
87
94
this .targetMetadataProvider = targetMetadataProvider ;
88
95
}
@@ -208,31 +215,40 @@ public void handleExistenceFilter(ExistenceFilterWatchChange watchChange) {
208
215
if (currentSize != expectedCount ) {
209
216
210
217
// Apply bloom filter to identify and mark removed documents.
211
- boolean bloomFilterApplied = this .applyBloomFilter (watchChange , currentSize );
218
+ BloomFilterApplicationStatus status = this .applyBloomFilter (watchChange , currentSize );
212
219
213
- if (! bloomFilterApplied ) {
220
+ if (status != BloomFilterApplicationStatus . SUCCESS ) {
214
221
// If bloom filter application fails, we reset the mapping and
215
222
// trigger re-run of the query.
216
223
resetTarget (targetId );
217
- pendingTargetResets .add (targetId );
224
+
225
+ QueryPurpose purpose =
226
+ status == BloomFilterApplicationStatus .FALSE_POSITIVE
227
+ ? QueryPurpose .EXISTENCE_FILTER_MISMATCH_BLOOM
228
+ : QueryPurpose .EXISTENCE_FILTER_MISMATCH ;
229
+
230
+ pendingTargetResets .put (targetId , purpose );
218
231
}
219
232
220
233
WatchChangeAggregatorTestingHooks .notifyOnExistenceFilterMismatch (
221
234
WatchChangeAggregatorTestingHooks .ExistenceFilterMismatchInfo .from (
222
- bloomFilterApplied , currentSize , watchChange .getExistenceFilter ()));
235
+ status == BloomFilterApplicationStatus .SUCCESS ,
236
+ currentSize ,
237
+ watchChange .getExistenceFilter ()));
223
238
}
224
239
}
225
240
}
226
241
}
227
242
228
- /** Returns whether a bloom filter removed the deleted documents successfully. */
229
- private boolean applyBloomFilter (ExistenceFilterWatchChange watchChange , int currentCount ) {
243
+ /** Apply bloom filter to remove the deleted documents, and return the application status. */
244
+ private BloomFilterApplicationStatus applyBloomFilter (
245
+ ExistenceFilterWatchChange watchChange , int currentCount ) {
230
246
int expectedCount = watchChange .getExistenceFilter ().getCount ();
231
247
com .google .firestore .v1 .BloomFilter unchangedNames =
232
248
watchChange .getExistenceFilter ().getUnchangedNames ();
233
249
234
250
if (unchangedNames == null || !unchangedNames .hasBits ()) {
235
- return false ;
251
+ return BloomFilterApplicationStatus . SKIPPED ;
236
252
}
237
253
238
254
byte [] bitmap = unchangedNames .getBits ().getBitmap ().toByteArray ();
@@ -248,12 +264,20 @@ private boolean applyBloomFilter(ExistenceFilterWatchChange watchChange, int cur
248
264
"Applying bloom filter failed: ("
249
265
+ e .getMessage ()
250
266
+ "); ignoring the bloom filter and falling back to full re-query." );
251
- return false ;
267
+ return BloomFilterApplicationStatus .SKIPPED ;
268
+ }
269
+
270
+ if (bloomFilter .getBitCount () == 0 ) {
271
+ return BloomFilterApplicationStatus .SKIPPED ;
252
272
}
253
273
254
274
int removedDocumentCount = this .filterRemovedDocuments (bloomFilter , watchChange .getTargetId ());
255
275
256
- return expectedCount == (currentCount - removedDocumentCount );
276
+ if (expectedCount != (currentCount - removedDocumentCount )) {
277
+ return BloomFilterApplicationStatus .FALSE_POSITIVE ;
278
+ }
279
+
280
+ return BloomFilterApplicationStatus .SUCCESS ;
257
281
}
258
282
259
283
/**
@@ -345,14 +369,14 @@ public RemoteEvent createRemoteEvent(SnapshotVersion snapshotVersion) {
345
369
new RemoteEvent (
346
370
snapshotVersion ,
347
371
Collections .unmodifiableMap (targetChanges ),
348
- Collections .unmodifiableSet (pendingTargetResets ),
372
+ Collections .unmodifiableMap (pendingTargetResets ),
349
373
Collections .unmodifiableMap (pendingDocumentUpdates ),
350
374
Collections .unmodifiableSet (resolvedLimboDocuments ));
351
375
352
376
// Re-initialize the current state to ensure that we do not modify the generated RemoteEvent.
353
377
pendingDocumentUpdates = new HashMap <>();
354
378
pendingDocumentTargetMapping = new HashMap <>();
355
- pendingTargetResets = new HashSet <>();
379
+ pendingTargetResets = new HashMap <>();
356
380
357
381
return remoteEvent ;
358
382
}
0 commit comments