@@ -17,7 +17,7 @@ use std::{
17
17
future,
18
18
rc:: Rc ,
19
19
sync:: {
20
- atomic:: { AtomicUsize , Ordering } ,
20
+ atomic:: { AtomicBool , AtomicUsize , Ordering } ,
21
21
Arc ,
22
22
} ,
23
23
time:: Duration ,
@@ -1057,7 +1057,6 @@ async fn cant_complete_activity_with_unset_result_payload() {
1057
1057
#[ rstest:: rstest]
1058
1058
#[ tokio:: test]
1059
1059
async fn graceful_shutdown ( #[ values( true , false ) ] at_max_outstanding : bool ) {
1060
- let _task_q = "q" ;
1061
1060
let grace_period = Duration :: from_millis ( 200 ) ;
1062
1061
let mut tasks = three_tasks ( ) ;
1063
1062
let mut mock_act_poller = mock_poller ( ) ;
@@ -1122,3 +1121,73 @@ async fn graceful_shutdown(#[values(true, false)] at_max_outstanding: bool) {
1122
1121
}
1123
1122
worker. drain_pollers_and_shutdown ( ) . await ;
1124
1123
}
1124
+
1125
+ #[ rstest:: rstest]
1126
+ #[ tokio:: test]
1127
+ async fn activities_must_be_flushed_to_server_on_shutdown ( #[ values( true , false ) ] use_grace : bool ) {
1128
+ crate :: telemetry:: test_telem_console ( ) ;
1129
+
1130
+ let grace_period = if use_grace {
1131
+ // Even though the grace period is shorter than the client call, the client call will still
1132
+ // go through. This is reasonable since the client has a timeout anyway, and it's unlikely
1133
+ // that a user *needs* an extremely short grace period (it'd be kind of pointless in that
1134
+ // case). They can always force-kill their worker in this situation.
1135
+ Duration :: from_millis ( 50 )
1136
+ } else {
1137
+ Duration :: from_secs ( 10 )
1138
+ } ;
1139
+ let shutdown_finished: & ' static AtomicBool = Box :: leak ( Box :: new ( AtomicBool :: new ( false ) ) ) ;
1140
+ let mut tasks = three_tasks ( ) ;
1141
+ let mut mock_act_poller = mock_poller ( ) ;
1142
+ mock_act_poller
1143
+ . expect_poll ( )
1144
+ . times ( 1 )
1145
+ . returning ( move || Some ( Ok ( tasks. pop_front ( ) . unwrap ( ) ) ) ) ;
1146
+ mock_act_poller
1147
+ . expect_poll ( )
1148
+ . times ( 1 )
1149
+ . returning ( move || None ) ;
1150
+ let mut mock_client = mock_manual_workflow_client ( ) ;
1151
+ mock_client
1152
+ . expect_complete_activity_task ( )
1153
+ . times ( 1 )
1154
+ . returning ( |_, _| {
1155
+ async {
1156
+ // We need some artificial delay here and there's nothing meaningful to sync with
1157
+ tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
1158
+ if shutdown_finished. load ( Ordering :: Acquire ) {
1159
+ panic ! ( "Shutdown must complete *after* server sees the activity completion" ) ;
1160
+ }
1161
+ Ok ( Default :: default ( ) )
1162
+ }
1163
+ . boxed ( )
1164
+ } ) ;
1165
+
1166
+ let mw = MockWorkerInputs {
1167
+ act_poller : Some ( Box :: from ( mock_act_poller) ) ,
1168
+ config : test_worker_cfg ( )
1169
+ . graceful_shutdown_period ( grace_period)
1170
+ . max_concurrent_at_polls ( 1_usize ) // Makes test logic simple
1171
+ . build ( )
1172
+ . unwrap ( ) ,
1173
+ ..Default :: default ( )
1174
+ } ;
1175
+ let worker = mock_worker ( MocksHolder :: from_mock_worker ( mock_client, mw) ) ;
1176
+
1177
+ let task = worker. poll_activity_task ( ) . await . unwrap ( ) ;
1178
+
1179
+ let shutdown_task = async {
1180
+ worker. drain_activity_poller_and_shutdown ( ) . await ;
1181
+ shutdown_finished. store ( true , Ordering :: Release ) ;
1182
+ } ;
1183
+ let complete_task = async {
1184
+ worker
1185
+ . complete_activity_task ( ActivityTaskCompletion {
1186
+ task_token : task. task_token ,
1187
+ result : Some ( ActivityExecutionResult :: ok ( "hi" . into ( ) ) ) ,
1188
+ } )
1189
+ . await
1190
+ . unwrap ( ) ;
1191
+ } ;
1192
+ join ! ( shutdown_task, complete_task) ;
1193
+ }
0 commit comments