@@ -433,15 +433,18 @@ export class WatchChangeAggregator {
433433 // raise a snapshot with `isFromCache:true`.
434434 if ( currentSize !== expectedCount ) {
435435 // Apply bloom filter to identify and mark removed documents.
436- const applyResult = this . applyBloomFilter ( watchChange , currentSize ) ;
436+ const bloomFilter = this . parseBloomFilter ( watchChange ) ;
437+ const status = bloomFilter
438+ ? this . applyBloomFilter ( bloomFilter , watchChange , currentSize )
439+ : BloomFilterApplicationStatus . Skipped ;
437440
438- if ( applyResult . status !== BloomFilterApplicationStatus . Success ) {
441+ if ( status !== BloomFilterApplicationStatus . Success ) {
439442 // If bloom filter application fails, we reset the mapping and
440443 // trigger re-run of the query.
441444 this . resetTarget ( targetId ) ;
442445
443446 const purpose : TargetPurpose =
444- applyResult . status === BloomFilterApplicationStatus . FalsePositive
447+ status === BloomFilterApplicationStatus . FalsePositive
445448 ? TargetPurpose . ExistenceFilterMismatchBloom
446449 : TargetPurpose . ExistenceFilterMismatch ;
447450 this . pendingTargetResets = this . pendingTargetResets . insert (
@@ -451,10 +454,11 @@ export class WatchChangeAggregator {
451454 }
452455 TestingHooks . instance ?. notifyOnExistenceFilterMismatch (
453456 createExistenceFilterMismatchInfoForTestingHooks (
454- applyResult . status ,
455- applyResult . bloomFilterMightContain ?? null ,
456457 currentSize ,
457- watchChange . existenceFilter
458+ watchChange . existenceFilter ,
459+ this . metadataProvider . getDatabaseId ( ) ,
460+ bloomFilter ,
461+ status
458462 )
459463 ) ;
460464 }
@@ -463,21 +467,15 @@ export class WatchChangeAggregator {
463467 }
464468
465469 /**
466- * Apply bloom filter to remove the deleted documents, and return the
467- * application status .
470+ * Parse the bloom filter from the "unchanged_names" field of an existence
471+ * filter .
468472 */
469- private applyBloomFilter (
470- watchChange : ExistenceFilterChange ,
471- currentCount : number
472- ) : {
473- status : BloomFilterApplicationStatus ;
474- bloomFilterMightContain ?: ( documentPath : string ) => boolean ;
475- } {
476- const { unchangedNames, count : expectedCount } =
477- watchChange . existenceFilter ;
478-
473+ private parseBloomFilter (
474+ watchChange : ExistenceFilterChange
475+ ) : BloomFilter | null {
476+ const unchangedNames = watchChange . existenceFilter . unchangedNames ;
479477 if ( ! unchangedNames || ! unchangedNames . bits ) {
480- return { status : BloomFilterApplicationStatus . Skipped } ;
478+ return null ;
481479 }
482480
483481 const {
@@ -495,7 +493,7 @@ export class WatchChangeAggregator {
495493 err . message +
496494 '); ignoring the bloom filter and falling back to full re-query.'
497495 ) ;
498- return { status : BloomFilterApplicationStatus . Skipped } ;
496+ return null ;
499497 } else {
500498 throw err ;
501499 }
@@ -511,46 +509,56 @@ export class WatchChangeAggregator {
511509 } else {
512510 logWarn ( 'Applying bloom filter failed: ' , err ) ;
513511 }
514- return { status : BloomFilterApplicationStatus . Skipped } ;
512+ return null ;
515513 }
516514
517- const bloomFilterMightContain = ( documentPath : string ) : boolean => {
518- const databaseId = this . metadataProvider . getDatabaseId ( ) ;
519- return bloomFilter . mightContain (
520- `projects/${ databaseId . projectId } /databases/${ databaseId . database } ` +
521- `/documents/${ documentPath } `
522- ) ;
523- } ;
524-
525515 if ( bloomFilter . bitCount === 0 ) {
526- return { status : BloomFilterApplicationStatus . Skipped } ;
516+ return null ;
527517 }
528518
519+ return bloomFilter ;
520+ }
521+
522+ /**
523+ * Apply bloom filter to remove the deleted documents, and return the
524+ * application status.
525+ */
526+ private applyBloomFilter (
527+ bloomFilter : BloomFilter ,
528+ watchChange : ExistenceFilterChange ,
529+ currentCount : number
530+ ) : BloomFilterApplicationStatus {
531+ const expectedCount = watchChange . existenceFilter . count ;
532+
529533 const removedDocumentCount = this . filterRemovedDocuments (
530- watchChange . targetId ,
531- bloomFilterMightContain
534+ bloomFilter ,
535+ watchChange . targetId
532536 ) ;
533537
534- const status =
535- expectedCount === currentCount - removedDocumentCount
536- ? BloomFilterApplicationStatus . Success
537- : BloomFilterApplicationStatus . FalsePositive ;
538- return { status, bloomFilterMightContain } ;
538+ return expectedCount === currentCount - removedDocumentCount
539+ ? BloomFilterApplicationStatus . Success
540+ : BloomFilterApplicationStatus . FalsePositive ;
539541 }
540542
541543 /**
542544 * Filter out removed documents based on bloom filter membership result and
543545 * return number of documents removed.
544546 */
545547 private filterRemovedDocuments (
546- targetId : number ,
547- bloomFilterMightContain : ( documentPath : string ) => boolean
548+ bloomFilter : BloomFilter ,
549+ targetId : number
548550 ) : number {
549551 const existingKeys = this . metadataProvider . getRemoteKeysForTarget ( targetId ) ;
550552 let removalCount = 0 ;
551553
552554 existingKeys . forEach ( key => {
553- if ( ! bloomFilterMightContain ( key . path . canonicalString ( ) ) ) {
555+ const databaseId = this . metadataProvider . getDatabaseId ( ) ;
556+ const documentPath =
557+ `projects/${ databaseId . projectId } ` +
558+ `/databases/${ databaseId . database } ` +
559+ `/documents/${ key . path . canonicalString ( ) } ` ;
560+
561+ if ( ! bloomFilter . mightContain ( documentPath ) ) {
554562 this . removeDocumentFromTarget ( targetId , key , /*updatedDocument=*/ null ) ;
555563 removalCount ++ ;
556564 }
@@ -835,27 +843,29 @@ function snapshotChangesMap(): SortedMap<DocumentKey, ChangeType> {
835843}
836844
837845function createExistenceFilterMismatchInfoForTestingHooks (
838- status : BloomFilterApplicationStatus ,
839- bloomFilterMightContain : null | ( ( documentPath : string ) => boolean ) ,
840846 localCacheCount : number ,
841- existenceFilter : ExistenceFilter
847+ existenceFilter : ExistenceFilter ,
848+ databaseId : DatabaseId ,
849+ bloomFilter : BloomFilter | null ,
850+ bloomFilterStatus : BloomFilterApplicationStatus
842851) : TestingHooksExistenceFilterMismatchInfo {
843852 const result : TestingHooksExistenceFilterMismatchInfo = {
844853 localCacheCount,
845- existenceFilterCount : existenceFilter . count
854+ existenceFilterCount : existenceFilter . count ,
855+ databaseId : databaseId . database ,
856+ projectId : databaseId . projectId
846857 } ;
847858
848859 const unchangedNames = existenceFilter . unchangedNames ;
849860 if ( unchangedNames ) {
850861 result . bloomFilter = {
851- applied : status === BloomFilterApplicationStatus . Success ,
862+ applied : bloomFilterStatus === BloomFilterApplicationStatus . Success ,
852863 hashCount : unchangedNames ?. hashCount ?? 0 ,
853864 bitmapLength : unchangedNames ?. bits ?. bitmap ?. length ?? 0 ,
854- padding : unchangedNames ?. bits ?. padding ?? 0
865+ padding : unchangedNames ?. bits ?. padding ?? 0 ,
866+ mightContain : ( value : string ) : boolean =>
867+ bloomFilter ?. mightContain ( value ) ?? false
855868 } ;
856- if ( bloomFilterMightContain ) {
857- result . bloomFilter . mightContain = bloomFilterMightContain ;
858- }
859869 }
860870
861871 return result ;
0 commit comments