@@ -273,7 +273,8 @@ describeSpec('Existence Filters:', [], () => {
273
273
) ;
274
274
275
275
/**
276
- * Test existence filter with bloom filter.
276
+ * Test existence filter with bloom filter. Existence filters below is sent mid-stream for
277
+ * testing simplicity.
277
278
*/
278
279
specTest (
279
280
'Full re-query is skipped when bloom filter can identify documents deleted' ,
@@ -626,9 +627,185 @@ describeSpec('Existence Filters:', [], () => {
626
627
// Doc0 to doc49 are deleted in the next sync.
627
628
. watchFilters ( [ query1 ] , docKeys . slice ( 0 , 50 ) , bloomFilterProto )
628
629
. watchSnapshots ( 2000 )
629
- // BloomFilter correctly identifies docs that deleted, skip full query.
630
+ // Bloom Filter correctly identifies docs that deleted, skips full query.
630
631
. expectEvents ( query1 , { fromCache : true } )
631
632
. expectLimboDocs ( ...docKeys . slice ( 50 ) )
632
633
) ;
633
634
} ) ;
635
+
636
+ specTest (
637
+ 'Resume a query with bloom filter when there is no document changes' ,
638
+ [ ] ,
639
+ ( ) => {
640
+ const query1 = query ( 'collection' ) ;
641
+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
642
+ const bloomFilterProto = generateBloomFilterProto ( {
643
+ contains : [ docA ] ,
644
+ notContains : [ ]
645
+ } ) ;
646
+ return (
647
+ spec ( )
648
+ . userListens ( query1 )
649
+ . watchAcksFull ( query1 , 1000 , docA )
650
+ . expectEvents ( query1 , { added : [ docA ] } )
651
+ . disableNetwork ( )
652
+ . expectEvents ( query1 , { fromCache : true } )
653
+ . enableNetwork ( )
654
+ . restoreListen ( query1 , 'resume-token-1000' )
655
+ . watchAcks ( query1 )
656
+ // Nothing happened while this client was disconnected.
657
+ // Bloom Filter includes docA as there are no changes since the resume token.
658
+ . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
659
+ // Expected count equals to documents in cache. Existence Filter matches.
660
+ . watchCurrents ( query1 , 'resume-token-2000' )
661
+ . watchSnapshots ( 2000 )
662
+ . expectEvents ( query1 , { fromCache : false } )
663
+ ) ;
664
+ }
665
+ ) ;
666
+
667
+ specTest (
668
+ 'Resume a query with bloom filter when new documents are added' ,
669
+ [ ] ,
670
+ ( ) => {
671
+ const query1 = query ( 'collection' ) ;
672
+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
673
+ const docB = doc ( 'collection/b' , 1000 , { v : 2 } ) ;
674
+ const bloomFilterProto = generateBloomFilterProto ( {
675
+ contains : [ docA , docB ] ,
676
+ notContains : [ ]
677
+ } ) ;
678
+ return (
679
+ spec ( )
680
+ . userListens ( query1 )
681
+ . watchAcksFull ( query1 , 1000 , docA )
682
+ . expectEvents ( query1 , { added : [ docA ] } )
683
+ . disableNetwork ( )
684
+ . expectEvents ( query1 , { fromCache : true } )
685
+ . enableNetwork ( )
686
+ . restoreListen ( query1 , 'resume-token-1000' )
687
+ . watchAcks ( query1 )
688
+ // While this client was disconnected, another client added docB.
689
+ . watchSends ( { affects : [ query1 ] } , docB )
690
+ // Bloom Filter includes all the documents that match the query, both
691
+ // those that haven't changed since the resume token and those newly added.
692
+ . watchFilters ( [ query1 ] , [ docA . key , docB . key ] , bloomFilterProto )
693
+ // Expected count equals to documents in cache. Existence Filter matches.
694
+ . watchCurrents ( query1 , 'resume-token-2000' )
695
+ . watchSnapshots ( 2000 )
696
+ . expectEvents ( query1 , { added : [ docB ] , fromCache : false } )
697
+ ) ;
698
+ }
699
+ ) ;
700
+
701
+ specTest (
702
+ 'Resume a query with bloom filter when existing docs are updated' ,
703
+ [ ] ,
704
+ ( ) => {
705
+ const query1 = query ( 'collection' , filter ( 'v' , '>=' , 1 ) ) ;
706
+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
707
+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
708
+ const updatedDocB = doc ( 'collection/b' , 1000 , { v : 2 } ) ;
709
+
710
+ const bloomFilterProto = generateBloomFilterProto ( {
711
+ contains : [ docA , updatedDocB ] ,
712
+ notContains : [ ]
713
+ } ) ;
714
+ return (
715
+ spec ( )
716
+ . userListens ( query1 )
717
+ . watchAcksFull ( query1 , 1000 , docA , docB )
718
+ . expectEvents ( query1 , { added : [ docA , docB ] } )
719
+ . disableNetwork ( )
720
+ . expectEvents ( query1 , { fromCache : true } )
721
+ . enableNetwork ( )
722
+ . restoreListen ( query1 , 'resume-token-1000' )
723
+ . watchAcks ( query1 )
724
+ // While this client was disconnected, another client updated fields in docB.
725
+ . watchSends ( { affects : [ query1 ] } , updatedDocB )
726
+ // Bloom Filter includes all the documents that match the query, both
727
+ // those that have changed since the resume token and those that have not.
728
+ . watchFilters ( [ query1 ] , [ docA . key , updatedDocB . key ] , bloomFilterProto )
729
+ // Expected count equals to documents in cache. Existence Filter matches.
730
+ . watchCurrents ( query1 , 'resume-token-2000' )
731
+ . watchSnapshots ( 2000 )
732
+ . expectEvents ( query1 , { fromCache : false } )
733
+ ) ;
734
+ }
735
+ ) ;
736
+
737
+ specTest (
738
+ 'Resume a query with bloom filter when documents are updated to no longer match the query' ,
739
+ [ ] ,
740
+ ( ) => {
741
+ const query1 = query ( 'collection' , filter ( 'v' , '==' , 1 ) ) ;
742
+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
743
+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
744
+ const updatedDocB = doc ( 'collection/b' , 2000 , { v : 2 } ) ;
745
+
746
+ const bloomFilterProto = generateBloomFilterProto ( {
747
+ contains : [ docA ] ,
748
+ notContains : [ docB ]
749
+ } ) ;
750
+ return (
751
+ spec ( )
752
+ . userListens ( query1 )
753
+ . watchAcksFull ( query1 , 1000 , docA , docB )
754
+ . expectEvents ( query1 , { added : [ docA , docB ] } )
755
+ . disableNetwork ( )
756
+ . expectEvents ( query1 , { fromCache : true } )
757
+ . enableNetwork ( )
758
+ . restoreListen ( query1 , 'resume-token-1000' )
759
+ . watchAcks ( query1 )
760
+ // While this client was disconnected, another client modified docB to no longer match the
761
+ // query. Bloom Filter includes only docA that matches the query since the resume token.
762
+ . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
763
+ . watchCurrents ( query1 , 'resume-token-2000' )
764
+ . watchSnapshots ( 2000 )
765
+ // Bloom Filter identifies that updatedDocB no longer matches the query, skips full query
766
+ // and puts updatedDocB into limbo directly.
767
+ . expectLimboDocs ( updatedDocB . key ) // updatedDocB is now in limbo.
768
+ ) ;
769
+ }
770
+ ) ;
771
+
772
+ specTest (
773
+ 'Resume a query with bloom filter when documents are added, removed and deleted' ,
774
+ [ ] ,
775
+ ( ) => {
776
+ const query1 = query ( 'collection' , filter ( 'v' , '==' , 1 ) ) ;
777
+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
778
+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
779
+ const updatedDocB = doc ( 'collection/b' , 2000 , { v : 2 } ) ;
780
+ const docC = doc ( 'collection/c' , 1000 , { v : 1 } ) ;
781
+ const docD = doc ( 'collection/d' , 1000 , { v : 1 } ) ;
782
+ const bloomFilterProto = generateBloomFilterProto ( {
783
+ contains : [ docA , docD ] ,
784
+ notContains : [ docB , docC ]
785
+ } ) ;
786
+
787
+ return (
788
+ spec ( )
789
+ . userListens ( query1 )
790
+ . watchAcksFull ( query1 , 1000 , docA , docB , docC )
791
+ . expectEvents ( query1 , { added : [ docA , docB , docC ] } )
792
+ . disableNetwork ( )
793
+ . expectEvents ( query1 , { fromCache : true } )
794
+ . enableNetwork ( )
795
+ . restoreListen ( query1 , 'resume-token-1000' )
796
+ . watchAcks ( query1 )
797
+ // While this client was disconnected, another client modified docB to no longer match the
798
+ // query, deleted docC and added docD.
799
+ . watchSends ( { affects : [ query1 ] } , docD )
800
+ // Bloom Filter includes all the documents that match the query.
801
+ . watchFilters ( [ query1 ] , [ docA . key , docD . key ] , bloomFilterProto )
802
+ . watchCurrents ( query1 , 'resume-token-2000' )
803
+ . watchSnapshots ( 2000 )
804
+ . expectEvents ( query1 , { added : [ docD ] , fromCache : true } )
805
+ // Bloom Filter identifies that updatedDocB and docC no longer match the query, skips full
806
+ // query and puts them into limbo directly.
807
+ . expectLimboDocs ( updatedDocB . key , docC . key ) // updatedDocB and docC is now in limbo.
808
+ ) ;
809
+ }
810
+ ) ;
634
811
} ) ;
0 commit comments