@@ -433,15 +433,18 @@ export class WatchChangeAggregator {
433
433
// raise a snapshot with `isFromCache:true`.
434
434
if ( currentSize !== expectedCount ) {
435
435
// 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 ;
437
440
438
- if ( applyResult . status !== BloomFilterApplicationStatus . Success ) {
441
+ if ( status !== BloomFilterApplicationStatus . Success ) {
439
442
// If bloom filter application fails, we reset the mapping and
440
443
// trigger re-run of the query.
441
444
this . resetTarget ( targetId ) ;
442
445
443
446
const purpose : TargetPurpose =
444
- applyResult . status === BloomFilterApplicationStatus . FalsePositive
447
+ status === BloomFilterApplicationStatus . FalsePositive
445
448
? TargetPurpose . ExistenceFilterMismatchBloom
446
449
: TargetPurpose . ExistenceFilterMismatch ;
447
450
this . pendingTargetResets = this . pendingTargetResets . insert (
@@ -451,10 +454,11 @@ export class WatchChangeAggregator {
451
454
}
452
455
TestingHooks . instance ?. notifyOnExistenceFilterMismatch (
453
456
createExistenceFilterMismatchInfoForTestingHooks (
454
- applyResult . status ,
455
- applyResult . bloomFilterMightContain ?? null ,
456
457
currentSize ,
457
- watchChange . existenceFilter
458
+ watchChange . existenceFilter ,
459
+ this . metadataProvider . getDatabaseId ( ) ,
460
+ bloomFilter ,
461
+ status
458
462
)
459
463
) ;
460
464
}
@@ -463,21 +467,15 @@ export class WatchChangeAggregator {
463
467
}
464
468
465
469
/**
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 .
468
472
*/
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 ;
479
477
if ( ! unchangedNames || ! unchangedNames . bits ) {
480
- return { status : BloomFilterApplicationStatus . Skipped } ;
478
+ return null ;
481
479
}
482
480
483
481
const {
@@ -495,7 +493,7 @@ export class WatchChangeAggregator {
495
493
err . message +
496
494
'); ignoring the bloom filter and falling back to full re-query.'
497
495
) ;
498
- return { status : BloomFilterApplicationStatus . Skipped } ;
496
+ return null ;
499
497
} else {
500
498
throw err ;
501
499
}
@@ -511,46 +509,56 @@ export class WatchChangeAggregator {
511
509
} else {
512
510
logWarn ( 'Applying bloom filter failed: ' , err ) ;
513
511
}
514
- return { status : BloomFilterApplicationStatus . Skipped } ;
512
+ return null ;
515
513
}
516
514
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
-
525
515
if ( bloomFilter . bitCount === 0 ) {
526
- return { status : BloomFilterApplicationStatus . Skipped } ;
516
+ return null ;
527
517
}
528
518
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
+
529
533
const removedDocumentCount = this . filterRemovedDocuments (
530
- watchChange . targetId ,
531
- bloomFilterMightContain
534
+ bloomFilter ,
535
+ watchChange . targetId
532
536
) ;
533
537
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 ;
539
541
}
540
542
541
543
/**
542
544
* Filter out removed documents based on bloom filter membership result and
543
545
* return number of documents removed.
544
546
*/
545
547
private filterRemovedDocuments (
546
- targetId : number ,
547
- bloomFilterMightContain : ( documentPath : string ) => boolean
548
+ bloomFilter : BloomFilter ,
549
+ targetId : number
548
550
) : number {
549
551
const existingKeys = this . metadataProvider . getRemoteKeysForTarget ( targetId ) ;
550
552
let removalCount = 0 ;
551
553
552
554
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 ) ) {
554
562
this . removeDocumentFromTarget ( targetId , key , /*updatedDocument=*/ null ) ;
555
563
removalCount ++ ;
556
564
}
@@ -835,27 +843,29 @@ function snapshotChangesMap(): SortedMap<DocumentKey, ChangeType> {
835
843
}
836
844
837
845
function createExistenceFilterMismatchInfoForTestingHooks (
838
- status : BloomFilterApplicationStatus ,
839
- bloomFilterMightContain : null | ( ( documentPath : string ) => boolean ) ,
840
846
localCacheCount : number ,
841
- existenceFilter : ExistenceFilter
847
+ existenceFilter : ExistenceFilter ,
848
+ databaseId : DatabaseId ,
849
+ bloomFilter : BloomFilter | null ,
850
+ bloomFilterStatus : BloomFilterApplicationStatus
842
851
) : TestingHooksExistenceFilterMismatchInfo {
843
852
const result : TestingHooksExistenceFilterMismatchInfo = {
844
853
localCacheCount,
845
- existenceFilterCount : existenceFilter . count
854
+ existenceFilterCount : existenceFilter . count ,
855
+ databaseId : databaseId . database ,
856
+ projectId : databaseId . projectId
846
857
} ;
847
858
848
859
const unchangedNames = existenceFilter . unchangedNames ;
849
860
if ( unchangedNames ) {
850
861
result . bloomFilter = {
851
- applied : status === BloomFilterApplicationStatus . Success ,
862
+ applied : bloomFilterStatus === BloomFilterApplicationStatus . Success ,
852
863
hashCount : unchangedNames ?. hashCount ?? 0 ,
853
864
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
855
868
} ;
856
- if ( bloomFilterMightContain ) {
857
- result . bloomFilter . mightContain = bloomFilterMightContain ;
858
- }
859
869
}
860
870
861
871
return result ;
0 commit comments