@@ -716,9 +716,12 @@ pub trait RemoteHandles<M: RemoteMessage>: Referable {}
716
716
717
717
#[ cfg( test) ]
718
718
mod tests {
719
+ use std:: collections:: HashMap ;
719
720
use std:: sync:: Mutex ;
720
721
use std:: time:: Duration ;
721
722
723
+ use timed_test:: async_timed_test;
724
+ use tokio:: sync:: mpsc;
722
725
use tokio:: time:: timeout;
723
726
724
727
use super :: * ;
@@ -728,6 +731,13 @@ mod tests {
728
731
use crate :: PortRef ;
729
732
use crate :: checkpoint:: CheckpointError ;
730
733
use crate :: checkpoint:: Checkpointable ;
734
+ use crate :: config;
735
+ use crate :: id;
736
+ use crate :: mailbox:: BoxedMailboxSender ;
737
+ use crate :: mailbox:: MailboxSender ;
738
+ use crate :: mailbox:: monitored_return_handle;
739
+ use crate :: proc:: SEQ_INFO ;
740
+ use crate :: proc:: SeqInfo ;
731
741
use crate :: test_utils:: pingpong:: PingPongActor ;
732
742
use crate :: test_utils:: pingpong:: PingPongActorParams ;
733
743
use crate :: test_utils:: pingpong:: PingPongMessage ;
@@ -1072,4 +1082,210 @@ mod tests {
1072
1082
handle. drain_and_stop ( ) . unwrap ( ) ;
1073
1083
handle. await ;
1074
1084
}
1085
+
1086
+ // Returning the sequence number assigned to the message.
1087
+ #[ derive( Debug ) ]
1088
+ #[ hyperactor:: export( handlers = [ String ] ) ]
1089
+ struct GetSeqActor ( PortRef < ( String , SeqInfo ) > ) ;
1090
+
1091
+ #[ async_trait]
1092
+ impl Actor for GetSeqActor {
1093
+ type Params = PortRef < ( String , SeqInfo ) > ;
1094
+
1095
+ async fn new ( params : PortRef < ( String , SeqInfo ) > ) -> Result < Self , anyhow:: Error > {
1096
+ Ok ( Self ( params) )
1097
+ }
1098
+ }
1099
+
1100
+ #[ async_trait]
1101
+ impl Handler < String > for GetSeqActor {
1102
+ async fn handle (
1103
+ & mut self ,
1104
+ cx : & Context < Self > ,
1105
+ message : String ,
1106
+ ) -> Result < ( ) , anyhow:: Error > {
1107
+ let Self ( port) = self ;
1108
+ let seq_info = cx. headers ( ) . get ( SEQ_INFO ) . unwrap ( ) ;
1109
+ port. send ( cx, ( message, seq_info. clone ( ) ) ) ?;
1110
+ Ok ( ( ) )
1111
+ }
1112
+ }
1113
+
1114
+ #[ async_timed_test( timeout_secs = 30 ) ]
1115
+ async fn test_sequencing_actor_handle_basic ( ) {
1116
+ let proc = Proc :: local ( ) ;
1117
+ let ( client, _) = proc. instance ( "client" ) . unwrap ( ) ;
1118
+ let ( tx, mut rx) = client. open_port ( ) ;
1119
+
1120
+ let actor_handle = proc
1121
+ . spawn :: < GetSeqActor > ( "get_seq" , tx. bind ( ) )
1122
+ . await
1123
+ . unwrap ( ) ;
1124
+ let actor_ref: ActorRef < GetSeqActor > = actor_handle. bind ( ) ;
1125
+
1126
+ let session_id = client. sequencer ( ) . session_id ( ) ;
1127
+ let mut expected_seq = 0 ;
1128
+ // Interleave messages sent through the handle and the reference.
1129
+ for _ in 0 ..10 {
1130
+ actor_handle. send ( & client, "" . to_string ( ) ) . unwrap ( ) ;
1131
+ expected_seq += 1 ;
1132
+ assert_eq ! (
1133
+ rx. recv( ) . await . unwrap( ) . 1 ,
1134
+ SeqInfo {
1135
+ session_id,
1136
+ seq: expected_seq,
1137
+ }
1138
+ ) ;
1139
+
1140
+ for _ in 0 ..2 {
1141
+ actor_ref. port ( ) . send ( & client, "" . to_string ( ) ) . unwrap ( ) ;
1142
+ expected_seq += 1 ;
1143
+ assert_eq ! (
1144
+ rx. recv( ) . await . unwrap( ) . 1 ,
1145
+ SeqInfo {
1146
+ session_id,
1147
+ seq: expected_seq,
1148
+ }
1149
+ ) ;
1150
+ }
1151
+ }
1152
+ }
1153
+
1154
+ // Adding a delay before sending the destination proc. Useful for tests
1155
+ // requiring latency injection.
1156
+ #[ derive( Debug ) ]
1157
+ struct DelayedMailboxSender {
1158
+ relay_tx : mpsc:: UnboundedSender < MessageEnvelope > ,
1159
+ }
1160
+
1161
+ impl DelayedMailboxSender {
1162
+ // Use a random latency between 0 and 1 second if the plan is empty.
1163
+ fn boxed ( dest_proc : Proc , latency_plan : HashMap < u64 , Duration > ) -> BoxedMailboxSender {
1164
+ let ( relay_tx, mut relay_rx) = mpsc:: unbounded_channel ( ) ;
1165
+ tokio:: spawn ( async move {
1166
+ let mut count = 0 ;
1167
+ while let Some ( envelope) = relay_rx. recv ( ) . await {
1168
+ count += 1 ;
1169
+
1170
+ let latency = if latency_plan. is_empty ( ) {
1171
+ Duration :: from_millis ( 1000 )
1172
+ } else {
1173
+ latency_plan. get ( & count) . unwrap ( ) . clone ( )
1174
+ } ;
1175
+
1176
+ let dest_proc_clone = dest_proc. clone ( ) ;
1177
+ tokio:: spawn ( async move {
1178
+ // Need Clock::sleep is an async function.
1179
+ #[ allow( clippy:: disallowed_methods) ]
1180
+ tokio:: time:: sleep ( latency) . await ;
1181
+ dest_proc_clone. post ( envelope, monitored_return_handle ( ) ) ;
1182
+ } ) ;
1183
+ }
1184
+ } ) ;
1185
+
1186
+ BoxedMailboxSender :: new ( Self { relay_tx } )
1187
+ }
1188
+ }
1189
+
1190
+ impl MailboxSender for DelayedMailboxSender {
1191
+ fn post_unchecked (
1192
+ & self ,
1193
+ envelope : MessageEnvelope ,
1194
+ _return_handle : PortHandle < Undeliverable < MessageEnvelope > > ,
1195
+ ) {
1196
+ self . relay_tx . send ( envelope) . unwrap ( ) ;
1197
+ }
1198
+ }
1199
+
1200
+ async fn assert_out_of_order_delivery (
1201
+ expected : Vec < ( String , u64 ) > ,
1202
+ latency_plan : HashMap < u64 , Duration > ,
1203
+ ) {
1204
+ let local_proc: Proc = Proc :: local ( ) ;
1205
+ let ( client, _) = local_proc. instance ( "local" ) . unwrap ( ) ;
1206
+ let ( tx, mut rx) = client. open_port ( ) ;
1207
+
1208
+ let handle = local_proc
1209
+ . spawn :: < GetSeqActor > ( "get_seq" , tx. bind ( ) )
1210
+ . await
1211
+ . unwrap ( ) ;
1212
+
1213
+ let actor_ref: ActorRef < GetSeqActor > = handle. bind ( ) ;
1214
+
1215
+ let remote_proc = Proc :: new (
1216
+ id ! ( remote[ 0 ] ) ,
1217
+ DelayedMailboxSender :: boxed ( local_proc. clone ( ) , latency_plan) ,
1218
+ ) ;
1219
+ let ( remote_client, _) = remote_proc. instance ( "remote" ) . unwrap ( ) ;
1220
+ // Send the messages out in the order of their expected sequence numbers.
1221
+ let mut messages = expected. clone ( ) ;
1222
+ messages. sort_by_key ( |v| v. 1 ) ;
1223
+ for ( message, _seq) in messages {
1224
+ actor_ref. send ( & remote_client, message) . unwrap ( ) ;
1225
+ }
1226
+ let session_id = remote_client. sequencer ( ) . session_id ( ) ;
1227
+ for expect in expected {
1228
+ let expected = (
1229
+ expect. 0 ,
1230
+ SeqInfo {
1231
+ session_id,
1232
+ seq : expect. 1 ,
1233
+ } ,
1234
+ ) ;
1235
+ assert_eq ! ( rx. recv( ) . await . unwrap( ) , expected) ;
1236
+ }
1237
+
1238
+ handle. drain_and_stop ( ) . unwrap ( ) ;
1239
+ handle. await ;
1240
+ }
1241
+
1242
+ // Send several messages, use DelayedMailboxSender and the latency plan to
1243
+ // ensure these messages will arrive at handler's workq in a determinstic
1244
+ // out-of-order way. Then verify the actor handler will still process these
1245
+ // messages based on their sending order if reordering buffer is enabled.
1246
+ #[ async_timed_test( timeout_secs = 30 ) ]
1247
+ async fn test_sequencing_actor_ref_out_of_order_deterministic ( ) {
1248
+ let config = config:: global:: lock ( ) ;
1249
+
1250
+ let latency_plan = maplit:: hashmap! {
1251
+ 1 => Duration :: from_millis( 1000 ) ,
1252
+ 2 => Duration :: from_millis( 0 ) ,
1253
+ } ;
1254
+
1255
+ // By disabling the actor side re-ordering buffer, the mssages will
1256
+ // be processed in the same order as they sent out.
1257
+ let _guard = config. override_key ( config:: ENABLE_CLIENT_SEQ_ASSIGNMENT , false ) ;
1258
+ assert_out_of_order_delivery (
1259
+ vec ! [ ( "second" . to_string( ) , 2 ) , ( "first" . to_string( ) , 1 ) ] ,
1260
+ latency_plan. clone ( ) ,
1261
+ )
1262
+ . await ;
1263
+
1264
+ // By enabling the actor side re-ordering buffer, the mssages will
1265
+ // be re-ordered before being processed.
1266
+ let _guard = config. override_key ( config:: ENABLE_CLIENT_SEQ_ASSIGNMENT , true ) ;
1267
+ assert_out_of_order_delivery (
1268
+ vec ! [ ( "first" . to_string( ) , 1 ) , ( "second" . to_string( ) , 2 ) ] ,
1269
+ latency_plan. clone ( ) ,
1270
+ )
1271
+ . await ;
1272
+ }
1273
+
1274
+ // Send a large nubmer of messages, use DelayedMailboxSender to ensure these
1275
+ // messages will arrive at handler's workq in a random order. Then verify the
1276
+ // actor handler will still process these messages based on their sending
1277
+ // order with reordering buffer enabled.
1278
+ #[ async_timed_test( timeout_secs = 30 ) ]
1279
+ async fn test_sequencing_actor_ref_out_of_order_random ( ) {
1280
+ let config = config:: global:: lock ( ) ;
1281
+
1282
+ // By enabling the actor side re-ordering buffer, the mssages will
1283
+ // be re-ordered before being processed.
1284
+ let _guard = config. override_key ( config:: ENABLE_CLIENT_SEQ_ASSIGNMENT , true ) ;
1285
+ let expected = ( 1 ..10000 )
1286
+ . map ( |i| ( format ! ( "msg{i}" ) , i) )
1287
+ . collect :: < Vec < _ > > ( ) ;
1288
+
1289
+ assert_out_of_order_delivery ( expected, HashMap :: new ( ) ) . await ;
1290
+ }
1075
1291
}
0 commit comments