@@ -99,6 +99,17 @@ mod moved_tests {
9999 conn
100100 }
101101
102+ fn configure_live_hibernated_pairs (
103+ ctx : & ActorContext ,
104+ pairs : impl IntoIterator < Item = ( & ' static [ u8 ] , & ' static [ u8 ] ) > ,
105+ ) {
106+ ctx. set_hibernated_connection_liveness_override (
107+ pairs
108+ . into_iter ( )
109+ . map ( |( gateway_id, request_id) | ( gateway_id. to_vec ( ) , request_id. to_vec ( ) ) ) ,
110+ ) ;
111+ }
112+
102113 #[ tokio:: test]
103114 async fn save_tick_respects_debounce_and_immediate_requests ( ) {
104115 let ctx = new_with_kv ( "actor-1" , "task-save" , Vec :: new ( ) , "local" , new_in_memory ( ) ) ;
@@ -272,6 +283,7 @@ mod moved_tests {
272283 request_headers : BTreeMap :: from ( [ ( "x-test" . to_owned ( ) , "true" . to_owned ( ) ) ] ) ,
273284 } ) ) ;
274285 ctx. add_conn ( hibernating_conn. clone ( ) ) ;
286+ configure_live_hibernated_pairs ( & ctx, [ ( b"gateway" . as_slice ( ) , b"request" . as_slice ( ) ) ] ) ;
275287
276288 let hibernating_conn_id = hibernating_conn. id ( ) . to_owned ( ) ;
277289 let factory = Arc :: new ( ActorFactory :: new (
@@ -381,6 +393,7 @@ mod moved_tests {
381393 request_headers : BTreeMap :: new ( ) ,
382394 } ) ) ;
383395 ctx. add_conn ( hibernating_conn. clone ( ) ) ;
396+ configure_live_hibernated_pairs ( & ctx, [ ( b"gateway" . as_slice ( ) , b"request" . as_slice ( ) ) ] ) ;
384397
385398 let hibernating_conn_id = hibernating_conn. id ( ) . to_owned ( ) ;
386399 let factory = Arc :: new ( ActorFactory :: new (
@@ -493,6 +506,7 @@ mod moved_tests {
493506 let ( _dispatch_tx, dispatch_rx) = mpsc:: channel ( 4 ) ;
494507 let ( events_tx, events_rx) = mpsc:: channel ( 4 ) ;
495508 ctx. configure_lifecycle_events ( Some ( events_tx) ) ;
509+ configure_live_hibernated_pairs ( & ctx, [ ( b"gateway" . as_slice ( ) , b"request" . as_slice ( ) ) ] ) ;
496510 let ( started_tx, started_rx) = oneshot:: channel ( ) ;
497511 let started_tx = Arc :: new ( Mutex :: new ( Some ( started_tx) ) ) ;
498512 let factory = Arc :: new ( ActorFactory :: new ( Default :: default ( ) , move |start| {
@@ -871,23 +885,10 @@ mod moved_tests {
871885 let ( _dispatch_tx, dispatch_rx) = mpsc:: channel ( 4 ) ;
872886 let ( events_tx, events_rx) = mpsc:: channel ( 4 ) ;
873887 ctx. configure_lifecycle_events ( Some ( events_tx) ) ;
874-
875- let stale_ctx = ctx. clone ( ) ;
876- tokio:: spawn ( async move {
877- for _ in 0 ..50 {
878- if stale_ctx. conns ( ) . len ( ) == 2 {
879- break ;
880- }
881- sleep ( Duration :: from_millis ( 5 ) ) . await ;
882- }
883- if let Some ( conn) = stale_ctx
884- . conns ( )
885- . into_iter ( )
886- . find ( |conn| conn. id ( ) == "conn-stale" )
887- {
888- let _ = conn. disconnect ( Some ( "stale" ) ) . await ;
889- }
890- } ) ;
888+ configure_live_hibernated_pairs (
889+ & ctx,
890+ [ ( b"gateway-live" . as_slice ( ) , b"request-live" . as_slice ( ) ) ] ,
891+ ) ;
891892
892893 let ( started_tx, started_rx) = oneshot:: channel ( ) ;
893894 let started_tx = Arc :: new ( Mutex :: new ( Some ( started_tx) ) ) ;
@@ -971,4 +972,113 @@ mod moved_tests {
971972 . await
972973 . expect ( "sleep stop should succeed" ) ;
973974 }
975+
976+ #[ tokio:: test]
977+ async fn wake_start_reaps_dead_hibernated_connections_without_engine_registration ( ) {
978+ let kv = new_in_memory ( ) ;
979+ let seed_ctx =
980+ new_with_kv ( "actor-wake-dead" , "task-wake" , Vec :: new ( ) , "local" , kv. clone ( ) ) ;
981+ let dead_conn = ConnHandle :: new ( "conn-dead" , Vec :: new ( ) , Vec :: new ( ) , true ) ;
982+ dead_conn. configure_hibernation ( Some ( HibernatableConnectionMetadata {
983+ gateway_id : b"gateway-dead" . to_vec ( ) ,
984+ request_id : b"request-dead" . to_vec ( ) ,
985+ server_message_index : 7 ,
986+ client_message_index : 11 ,
987+ request_path : "/ws" . to_owned ( ) ,
988+ request_headers : BTreeMap :: new ( ) ,
989+ } ) ) ;
990+ seed_ctx. add_conn ( dead_conn. clone ( ) ) ;
991+ seed_ctx
992+ . save_state ( vec ! [ StateDelta :: ConnHibernation {
993+ conn: dead_conn. id( ) . into( ) ,
994+ bytes: vec![ 9 , 8 , 7 ] ,
995+ } ] )
996+ . await
997+ . expect ( "seed hibernation should persist" ) ;
998+
999+ let ctx =
1000+ new_with_kv ( "actor-wake-dead" , "task-wake" , Vec :: new ( ) , "local" , kv. clone ( ) ) ;
1001+ let ( _lifecycle_tx, lifecycle_rx) = mpsc:: channel ( 4 ) ;
1002+ let ( _dispatch_tx, dispatch_rx) = mpsc:: channel ( 4 ) ;
1003+ let ( events_tx, events_rx) = mpsc:: channel ( 4 ) ;
1004+ ctx. configure_lifecycle_events ( Some ( events_tx) ) ;
1005+ ctx. set_hibernated_connection_liveness_override ( std:: iter:: empty ( ) ) ;
1006+
1007+ let ( started_tx, started_rx) = oneshot:: channel ( ) ;
1008+ let started_tx = Arc :: new ( Mutex :: new ( Some ( started_tx) ) ) ;
1009+ let factory = Arc :: new ( ActorFactory :: new ( Default :: default ( ) , move |start| {
1010+ let started_tx = started_tx. clone ( ) ;
1011+ Box :: pin ( async move {
1012+ let mut events = start. events ;
1013+ started_tx
1014+ . lock ( )
1015+ . expect ( "started sender lock poisoned" )
1016+ . take ( )
1017+ . expect ( "started sender should exist" )
1018+ . send ( start. hibernated . into_iter ( ) . map ( |( conn, _) | conn. id ( ) . to_owned ( ) ) . collect :: < Vec < _ > > ( ) )
1019+ . expect ( "started info should send" ) ;
1020+ while let Some ( event) = events. recv ( ) . await {
1021+ match event {
1022+ ActorEvent :: SaveTick { reply } => {
1023+ reply. send ( Ok ( Vec :: new ( ) ) ) ;
1024+ }
1025+ ActorEvent :: Sleep { reply } | ActorEvent :: Destroy { reply } => {
1026+ reply. send ( Ok ( Vec :: new ( ) ) ) ;
1027+ break ;
1028+ }
1029+ ActorEvent :: ConnectionOpen { .. } => {
1030+ panic ! ( "dead hibernated connection should not refire ConnectionOpen" ) ;
1031+ }
1032+ _ => { }
1033+ }
1034+ }
1035+ Ok ( ( ) )
1036+ } )
1037+ } ) ) ;
1038+
1039+ let mut task = ActorTask :: new (
1040+ "actor-wake-dead" . into ( ) ,
1041+ 0 ,
1042+ lifecycle_rx,
1043+ dispatch_rx,
1044+ events_rx,
1045+ factory,
1046+ ctx. clone ( ) ,
1047+ None ,
1048+ None ,
1049+ ) ;
1050+ let ( start_tx, start_rx) = oneshot:: channel ( ) ;
1051+ task
1052+ . handle_lifecycle ( LifecycleCommand :: Start { reply : start_tx } )
1053+ . await ;
1054+ start_rx
1055+ . await
1056+ . expect ( "start reply should send" )
1057+ . expect ( "start should succeed" ) ;
1058+
1059+ assert_eq ! ( started_rx. await . expect( "start info should send" ) , Vec :: <String >:: new( ) ) ;
1060+ assert ! ( ctx. conns( ) . is_empty( ) ) ;
1061+
1062+ task
1063+ . handle_event ( crate :: actor:: task:: LifecycleEvent :: SaveRequested {
1064+ immediate : false ,
1065+ } )
1066+ . await ;
1067+ task. on_state_save_tick ( ) . await ;
1068+
1069+ let last_batch = kv
1070+ . test_last_apply_batch ( )
1071+ . expect ( "last apply batch should be recorded" ) ;
1072+ assert_eq ! ( last_batch. deletes, vec![ make_connection_key( "conn-dead" ) ] ) ;
1073+ assert ! (
1074+ kv. get( & make_connection_key( "conn-dead" ) )
1075+ . await
1076+ . expect( "persisted connection lookup should succeed" )
1077+ . is_none( )
1078+ ) ;
1079+
1080+ task. handle_stop ( StopReason :: Sleep )
1081+ . await
1082+ . expect ( "sleep stop should succeed" ) ;
1083+ }
9741084}
0 commit comments