@@ -473,182 +473,162 @@ describeSpec('Existence Filters:', [], () => {
473473 }
474474 ) ;
475475
476- specTest (
477- 'Full re-query is triggered when bloom filter is empty' ,
478- [ ] ,
479- ( ) => {
480- const query1 = query ( 'collection' ) ;
481- const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
482- const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
483-
484- //Generate an empty bloom filter.
485- const bloomFilterProto = generateBloomFilterProto ( {
486- contains : [ ] ,
487- notContains : [ ] ,
488- bitCount : 0 ,
489- hashCount : 0
490- } ) ;
491-
492- return (
493- spec ( )
494- . userListens ( query1 )
495- . watchAcksFull ( query1 , 1000 , docA , docB )
496- . expectEvents ( query1 , { added : [ docA , docB ] } )
497- // DocB is deleted in the next sync.
498- . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
499- . watchSnapshots ( 2000 )
500- // Re-run query is triggered.
501- . expectEvents ( query1 , { fromCache : true } )
502- . expectActiveTargets ( {
503- query : query1 ,
504- resumeToken : '' ,
505- targetPurpose : TargetPurpose . ExistenceFilterMismatch
506- } )
507- ) ;
508- }
509- ) ;
476+ specTest ( 'Full re-query is triggered when bloom filter is empty' , [ ] , ( ) => {
477+ const query1 = query ( 'collection' ) ;
478+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
479+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
510480
511- specTest (
512- 'Same documents can have different bloom filters' ,
513- [ ] ,
514- ( ) => {
515- const query1 = query ( 'collection' , filter ( 'v' , '<=' , 2 ) ) ;
516- const query2 = query ( 'collection' , filter ( 'v' , '>=' , 2 ) ) ;
481+ //Generate an empty bloom filter.
482+ const bloomFilterProto = generateBloomFilterProto ( {
483+ contains : [ ] ,
484+ notContains : [ ] ,
485+ bitCount : 0 ,
486+ hashCount : 0
487+ } ) ;
517488
518- const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
519- const docB = doc ( 'collection/b' , 1000 , { v : 2 } ) ;
520- const docC = doc ( 'collection/c' , 1000 , { v : 3 } ) ;
489+ return (
490+ spec ( )
491+ . userListens ( query1 )
492+ . watchAcksFull ( query1 , 1000 , docA , docB )
493+ . expectEvents ( query1 , { added : [ docA , docB ] } )
494+ // DocB is deleted in the next sync.
495+ . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
496+ . watchSnapshots ( 2000 )
497+ // Re-run query is triggered.
498+ . expectEvents ( query1 , { fromCache : true } )
499+ . expectActiveTargets ( {
500+ query : query1 ,
501+ resumeToken : '' ,
502+ targetPurpose : TargetPurpose . ExistenceFilterMismatch
503+ } )
504+ ) ;
505+ } ) ;
521506
522- const bloomFilterProto1 = generateBloomFilterProto ( {
523- contains : [ docB ] ,
524- notContains : [ docA , docC ] ,
525- bitCount : 5 ,
526- hashCount : 2
527- } ) ;
528- const bloomFilterProto2 = generateBloomFilterProto ( {
529- contains : [ docB ] ,
530- notContains : [ docA , docC ] ,
531- bitCount : 4 ,
532- hashCount : 1
533- } ) ;
534- return (
535- spec ( )
536- . userListens ( query1 )
537- . watchAcksFull ( query1 , 1000 , docA , docB )
538- . expectEvents ( query1 , { added : [ docA , docB ] } )
539- . userListens ( query2 )
540- . expectEvents ( query2 , { added : [ docB ] , fromCache : true } )
541- . watchAcksFull ( query2 , 1001 , docB , docC )
542- . expectEvents ( query2 , { added : [ docC ] } )
507+ specTest ( 'Same documents can have different bloom filters' , [ ] , ( ) => {
508+ const query1 = query ( 'collection' , filter ( 'v' , '<=' , 2 ) ) ;
509+ const query2 = query ( 'collection' , filter ( 'v' , '>=' , 2 ) ) ;
543510
544- // DocA is deleted in the next sync for query1.
545- . watchFilters ( [ query1 ] , [ docB . key ] , bloomFilterProto1 )
546- . watchSnapshots ( 2000 )
547- // BloomFilter identify docA is deleted, skip full query.
548- . expectEvents ( query1 , { fromCache : true } )
549- . expectLimboDocs ( docA . key ) // DocA is now in limbo.
550-
551- // DocC is deleted in the next sync for query2.
552- . watchFilters ( [ query2 ] , [ docB . key ] , bloomFilterProto2 )
553- . watchSnapshots ( 3000 )
554- // BloomFilter identify docC is deleted, skip full query.
555- . expectEvents ( query2 , { fromCache : true } )
556- . expectLimboDocs ( docA . key , docC . key ) // DocC is now in limbo.
557- ) ;
558- }
559- ) ;
511+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
512+ const docB = doc ( 'collection/b' , 1000 , { v : 2 } ) ;
513+ const docC = doc ( 'collection/c' , 1000 , { v : 3 } ) ;
514+
515+ const bloomFilterProto1 = generateBloomFilterProto ( {
516+ contains : [ docB ] ,
517+ notContains : [ docA , docC ] ,
518+ bitCount : 5 ,
519+ hashCount : 2
520+ } ) ;
521+ const bloomFilterProto2 = generateBloomFilterProto ( {
522+ contains : [ docB ] ,
523+ notContains : [ docA , docC ] ,
524+ bitCount : 4 ,
525+ hashCount : 1
526+ } ) ;
527+ return (
528+ spec ( )
529+ . userListens ( query1 )
530+ . watchAcksFull ( query1 , 1000 , docA , docB )
531+ . expectEvents ( query1 , { added : [ docA , docB ] } )
532+ . userListens ( query2 )
533+ . expectEvents ( query2 , { added : [ docB ] , fromCache : true } )
534+ . watchAcksFull ( query2 , 1001 , docB , docC )
535+ . expectEvents ( query2 , { added : [ docC ] } )
560536
561- specTest (
562- 'Bloom filter is handled at global snapshot' ,
563- [ ] ,
564- ( ) => {
565- const query1 = query ( 'collection' ) ;
566- const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
567- const docB = doc ( 'collection/b' , 2000 , { v : 2 } ) ;
568- const docC = doc ( 'collection/c' , 3000 , { v : 3 } ) ;
537+ // DocA is deleted in the next sync for query1.
538+ . watchFilters ( [ query1 ] , [ docB . key ] , bloomFilterProto1 )
539+ . watchSnapshots ( 2000 )
540+ // BloomFilter identify docA is deleted, skip full query.
541+ . expectEvents ( query1 , { fromCache : true } )
542+ . expectLimboDocs ( docA . key ) // DocA is now in limbo.
543+
544+ // DocC is deleted in the next sync for query2.
545+ . watchFilters ( [ query2 ] , [ docB . key ] , bloomFilterProto2 )
546+ . watchSnapshots ( 3000 )
547+ // BloomFilter identify docC is deleted, skip full query.
548+ . expectEvents ( query2 , { fromCache : true } )
549+ . expectLimboDocs ( docA . key , docC . key ) // DocC is now in limbo.
550+ ) ;
551+ } ) ;
569552
570- const bloomFilterProto = generateBloomFilterProto ( {
571- contains : [ docA ] ,
572- notContains : [ docB ]
573- } ) ;
553+ specTest ( 'Bloom filter is handled at global snapshot' , [ ] , ( ) => {
554+ const query1 = query ( 'collection' ) ;
555+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
556+ const docB = doc ( 'collection/b' , 2000 , { v : 2 } ) ;
557+ const docC = doc ( 'collection/c' , 3000 , { v : 3 } ) ;
574558
575- return (
576- spec ( )
577- . userListens ( query1 )
578- . watchAcksFull ( query1 , 1000 , docA , docB )
579- . expectEvents ( query1 , { added : [ docA , docB ] } )
580- // Send a mismatching existence filter with one document, but don't
581- // send a new global snapshot. We should not see an event until we
582- // receive the snapshot.
583- . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
584- . watchSends ( { affects : [ query1 ] } , docC )
585- . watchSnapshots ( 2000 )
586- . expectEvents ( query1 , { added : [ docC ] , fromCache : true } )
587- // Re-run of the query1 is skipped, docB is in limbo.
588- . expectLimboDocs ( docB . key )
589- ) ;
590- }
591- ) ;
559+ const bloomFilterProto = generateBloomFilterProto ( {
560+ contains : [ docA ] ,
561+ notContains : [ docB ]
562+ } ) ;
592563
593- specTest (
594- 'Bloom filter limbo resolution is denied' ,
595- [ ] ,
596- ( ) => {
597- const query1 = query ( 'collection' ) ;
598- const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
599- const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
600- const bloomFilterProto = generateBloomFilterProto ( {
601- contains : [ docA ] ,
602- notContains : [ docB ]
603- } ) ;
604- return spec ( )
564+ return (
565+ spec ( )
605566 . userListens ( query1 )
606567 . watchAcksFull ( query1 , 1000 , docA , docB )
607568 . expectEvents ( query1 , { added : [ docA , docB ] } )
569+ // Send a mismatching existence filter with one document, but don't
570+ // send a new global snapshot. We should not see an event until we
571+ // receive the snapshot.
608572 . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
573+ . watchSends ( { affects : [ query1 ] } , docC )
609574 . watchSnapshots ( 2000 )
610- . expectEvents ( query1 , { fromCache : true } )
611- . expectLimboDocs ( docB . key ) // DocB is now in limbo.
612- . watchRemoves (
613- newQueryForPath ( docB . key . path ) ,
614- new RpcError ( Code . PERMISSION_DENIED , 'no' )
615- )
616- . expectLimboDocs ( ) // DocB is no longer in limbo.
617- . expectEvents ( query1 , {
618- removed : [ docB ]
619- } ) ;
620- }
621- ) ;
622-
623- specTest (
624- 'Bloom filter with large size works as expected' ,
625- [ ] ,
626- ( ) => {
627- const query1 = query ( 'collection' ) ;
628- const docs = [ ] ;
629- for ( let i = 0 ; i < 100 ; i ++ ) {
630- docs . push ( doc ( `collection/doc${ i } ` , 1000 , { v : 1 } ) ) ;
631- }
632- const docKeys = docs . map ( item => item . key ) ;
575+ . expectEvents ( query1 , { added : [ docC ] , fromCache : true } )
576+ // Re-run of the query1 is skipped, docB is in limbo.
577+ . expectLimboDocs ( docB . key )
578+ ) ;
579+ } ) ;
633580
634- const bloomFilterProto = generateBloomFilterProto ( {
635- contains : docs . slice ( 0 , 50 ) ,
636- notContains : docs . slice ( 50 ) ,
637- bitCount : 1000 ,
638- hashCount : 16
581+ specTest ( 'Bloom filter limbo resolution is denied' , [ ] , ( ) => {
582+ const query1 = query ( 'collection' ) ;
583+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
584+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
585+ const bloomFilterProto = generateBloomFilterProto ( {
586+ contains : [ docA ] ,
587+ notContains : [ docB ]
588+ } ) ;
589+ return spec ( )
590+ . userListens ( query1 )
591+ . watchAcksFull ( query1 , 1000 , docA , docB )
592+ . expectEvents ( query1 , { added : [ docA , docB ] } )
593+ . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
594+ . watchSnapshots ( 2000 )
595+ . expectEvents ( query1 , { fromCache : true } )
596+ . expectLimboDocs ( docB . key ) // DocB is now in limbo.
597+ . watchRemoves (
598+ newQueryForPath ( docB . key . path ) ,
599+ new RpcError ( Code . PERMISSION_DENIED , 'no' )
600+ )
601+ . expectLimboDocs ( ) // DocB is no longer in limbo.
602+ . expectEvents ( query1 , {
603+ removed : [ docB ]
639604 } ) ;
640- return (
641- spec ( )
642- . userListens ( query1 )
643- . watchAcksFull ( query1 , 1000 , ...docs )
644- . expectEvents ( query1 , { added : docs } )
645- // Doc0 to doc49 are deleted in the next sync.
646- . watchFilters ( [ query1 ] , docKeys . slice ( 0 , 50 ) , bloomFilterProto )
647- . watchSnapshots ( 2000 )
648- // BloomFilter correctly identifies docs that deleted, skip full query.
649- . expectEvents ( query1 , { fromCache : true } )
650- . expectLimboDocs ( ...docKeys . slice ( 50 ) )
651- ) ;
605+ } ) ;
606+
607+ specTest ( 'Bloom filter with large size works as expected' , [ ] , ( ) => {
608+ const query1 = query ( 'collection' ) ;
609+ const docs = [ ] ;
610+ for ( let i = 0 ; i < 100 ; i ++ ) {
611+ docs . push ( doc ( `collection/doc${ i } ` , 1000 , { v : 1 } ) ) ;
652612 }
653- ) ;
613+ const docKeys = docs . map ( item => item . key ) ;
614+
615+ const bloomFilterProto = generateBloomFilterProto ( {
616+ contains : docs . slice ( 0 , 50 ) ,
617+ notContains : docs . slice ( 50 ) ,
618+ bitCount : 1000 ,
619+ hashCount : 16
620+ } ) ;
621+ return (
622+ spec ( )
623+ . userListens ( query1 )
624+ . watchAcksFull ( query1 , 1000 , ...docs )
625+ . expectEvents ( query1 , { added : docs } )
626+ // Doc0 to doc49 are deleted in the next sync.
627+ . watchFilters ( [ query1 ] , docKeys . slice ( 0 , 50 ) , bloomFilterProto )
628+ . watchSnapshots ( 2000 )
629+ // BloomFilter correctly identifies docs that deleted, skip full query.
630+ . expectEvents ( query1 , { fromCache : true } )
631+ . expectLimboDocs ( ...docKeys . slice ( 50 ) )
632+ ) ;
633+ } ) ;
654634} ) ;
0 commit comments