@@ -663,6 +663,7 @@ mod tests {
663663 dt_data:: { DtData , DtItem } ,
664664 position:: Position ,
665665 redis:: redis_entry:: RedisEntry ,
666+ zk:: { zk_entry:: ZkEntry , zk_event_type:: ZkEventType , zk_stat:: ZkStat } ,
666667 } ;
667668 use dt_connector:: extractor:: resumer:: utils:: ResumerUtil ;
668669
@@ -690,6 +691,34 @@ mod tests {
690691 }
691692 }
692693
694+ fn zk_item ( position : Position ) -> DtItem {
695+ DtItem {
696+ dt_data : DtData :: Zk {
697+ entry : ZkEntry {
698+ path : "/app/service" . to_string ( ) ,
699+ data : Some ( b"value" . to_vec ( ) ) ,
700+ stat : ZkStat :: default ( ) ,
701+ ephemeral : false ,
702+ event_type : ZkEventType :: Updated ,
703+ source_id : "node-a" . to_string ( ) ,
704+ source_order_millis : 1 ,
705+ source_zxid : 1 ,
706+ order_origin : Default :: default ( ) ,
707+ } ,
708+ } ,
709+ position,
710+ data_origin_node : String :: new ( ) ,
711+ }
712+ }
713+
714+ fn heartbeat_item ( position : Position ) -> DtItem {
715+ DtItem {
716+ dt_data : DtData :: Heartbeat { } ,
717+ position,
718+ data_origin_node : String :: new ( ) ,
719+ }
720+ }
721+
693722 #[ test]
694723 fn fetch_raw_collects_latest_position_per_redis_node ( ) {
695724 let mut pending_snapshot_finished = HashMap :: new ( ) ;
@@ -713,4 +742,31 @@ mod tests {
713742 assert_eq ! ( by_key. get( "redis-node-node-1" ) , Some ( & node_1_new) ) ;
714743 assert_eq ! ( by_key. get( "redis-node-node-2" ) , Some ( & node_2) ) ;
715744 }
745+
746+ #[ test]
747+ fn fetch_raw_advances_zk_position_only_on_trailing_heartbeat ( ) {
748+ let mut pending_snapshot_finished = HashMap :: new ( ) ;
749+ let final_position = Position :: Zk {
750+ path_versions : HashMap :: from ( [ ( "/app/service" . to_string ( ) , ( 1 , 10 ) ) ] ) ,
751+ last_scan_timestamp : 100 ,
752+ high_water_zxid : 10 ,
753+ total_paths : 1 ,
754+ } ;
755+ let partial_data = vec ! [ zk_item( Position :: None ) ] ;
756+
757+ let ( _, partial_position, partial_commits) =
758+ BasePipeline :: fetch_raw ( & partial_data, & mut pending_snapshot_finished) ;
759+ assert_eq ! ( partial_position, Some ( Position :: None ) ) ;
760+ assert ! ( partial_commits. is_empty( ) ) ;
761+
762+ let complete_data = vec ! [
763+ zk_item( Position :: None ) ,
764+ heartbeat_item( final_position. clone( ) ) ,
765+ ] ;
766+ let ( _, complete_position, complete_commits) =
767+ BasePipeline :: fetch_raw ( & complete_data, & mut pending_snapshot_finished) ;
768+
769+ assert_eq ! ( complete_position, Some ( final_position. clone( ) ) ) ;
770+ assert_eq ! ( complete_commits, vec![ final_position] ) ;
771+ }
716772}
0 commit comments