@@ -1277,6 +1277,30 @@ impl cap::sealed::CanOpenPort for Mailbox {
1277
1277
}
1278
1278
}
1279
1279
1280
+ #[ derive( Default ) ]
1281
+ struct SplitPortBuffer ( Vec < Serialized > ) ;
1282
+
1283
+ impl SplitPortBuffer {
1284
+ /// Push a new item to the buffer, and optionally return any items that should
1285
+ /// be flushed.
1286
+ fn push ( & mut self , serialized : Serialized ) -> Option < Vec < Serialized > > {
1287
+ static HYPERACTOR_SPLIT_MAX_BUFFER_SIZE : OnceLock < usize > = OnceLock :: new ( ) ;
1288
+ let limit = HYPERACTOR_SPLIT_MAX_BUFFER_SIZE . get_or_init ( || {
1289
+ std:: env:: var ( "HYPERACTOR_SPLIT_MAX_BUFFER_SIZE" )
1290
+ . ok ( )
1291
+ . and_then ( |val| val. parse :: < usize > ( ) . ok ( ) )
1292
+ . unwrap_or ( 5 )
1293
+ } ) ;
1294
+
1295
+ self . 0 . push ( serialized) ;
1296
+ if & self . 0 . len ( ) >= limit {
1297
+ Some ( std:: mem:: take ( & mut self . 0 ) )
1298
+ } else {
1299
+ None
1300
+ }
1301
+ }
1302
+ }
1303
+
1280
1304
impl cap:: sealed:: CanSplitPort for Mailbox {
1281
1305
fn split ( & self , port_id : PortId , reducer_typehash : Option < u64 > ) -> PortId {
1282
1306
fn post ( mailbox : & Mailbox , port_id : PortId , msg : Serialized ) {
@@ -1302,23 +1326,22 @@ impl cap::sealed::CanSplitPort for Mailbox {
1302
1326
Ok ( ( ) )
1303
1327
} ) ,
1304
1328
Some ( r) => {
1305
- let buffer = Arc :: new ( Mutex :: new ( Vec :: < Serialized > :: new ( ) ) ) ;
1329
+ let buffer = Mutex :: new ( SplitPortBuffer :: default ( ) ) ;
1306
1330
Box :: new ( move |serialized : Serialized | {
1307
1331
// Hold the lock until messages are sent. This is to avoid another
1308
1332
// invocation of this method trying to send message concurrently and
1309
1333
// cause messages delivered out of order.
1310
1334
let mut buf = buffer. lock ( ) . unwrap ( ) ;
1311
- buf. push ( serialized) ;
1312
- // TODO(pzhang) add policy and use this buffer
1313
- let buffered = std:: mem:: take ( & mut * buf) ;
1314
- let reduced = r. reduce_updates ( buffered) . map_err ( |( e, mut b) | {
1315
- (
1316
- b. pop ( )
1317
- . expect ( "there should be at least one update from buffer" ) ,
1318
- e,
1319
- )
1320
- } ) ?;
1321
- post ( & mailbox, port_id. clone ( ) , reduced) ;
1335
+ if let Some ( buffered) = buf. push ( serialized) {
1336
+ let reduced = r. reduce_updates ( buffered) . map_err ( |( e, mut b) | {
1337
+ (
1338
+ b. pop ( )
1339
+ . expect ( "there should be at least one update from buffer" ) ,
1340
+ e,
1341
+ )
1342
+ } ) ?;
1343
+ post ( & mailbox, port_id. clone ( ) , reduced) ;
1344
+ }
1322
1345
Ok ( ( ) )
1323
1346
} )
1324
1347
}
@@ -2208,6 +2231,7 @@ mod tests {
2208
2231
use std:: time:: Duration ;
2209
2232
2210
2233
use timed_test:: async_timed_test;
2234
+ use tracing:: Level ;
2211
2235
2212
2236
use super :: * ;
2213
2237
use crate :: Actor ;
@@ -2224,6 +2248,7 @@ mod tests {
2224
2248
use crate :: proc:: Proc ;
2225
2249
use crate :: reference:: ProcId ;
2226
2250
use crate :: reference:: WorldId ;
2251
+ use crate :: test_utils:: tracing:: set_tracing_env_filter;
2227
2252
2228
2253
#[ test]
2229
2254
fn test_error ( ) {
@@ -2937,6 +2962,9 @@ mod tests {
2937
2962
2938
2963
#[ async_timed_test( timeout_secs = 30 ) ]
2939
2964
async fn test_split_port_id_sum_reducer ( ) {
2965
+ std:: env:: set_var ( "HYPERACTOR_SPLIT_MAX_BUFFER_SIZE" , "1" ) ;
2966
+ set_tracing_env_filter ( Level :: INFO ) ;
2967
+
2940
2968
let sum_accumulator = accum:: sum :: < u64 > ( ) ;
2941
2969
let reducer_typehash = sum_accumulator. reducer_typehash ( ) ;
2942
2970
let Setup {
@@ -2965,4 +2993,35 @@ mod tests {
2965
2993
let msg = receiver. try_recv ( ) . unwrap ( ) ;
2966
2994
assert_eq ! ( msg, None ) ;
2967
2995
}
2996
+
2997
+ #[ async_timed_test( timeout_secs = 30 ) ]
2998
+ async fn test_split_port_id_every_n_messages ( ) {
2999
+ set_tracing_env_filter ( Level :: INFO ) ;
3000
+ let actor = Mailbox :: new (
3001
+ id ! ( test[ 0 ] . actor) ,
3002
+ BoxedMailboxSender :: new ( PanickingMailboxSender ) ,
3003
+ ) ;
3004
+ let ( port_handle, mut receiver) = actor. open_port :: < u64 > ( ) ;
3005
+ let port_id = port_handle. bind ( ) . port_id ( ) . clone ( ) ;
3006
+ // Split it
3007
+ let reducer_typehash = accum:: sum :: < u64 > ( ) . reducer_typehash ( ) ;
3008
+ let split_port_id = port_id. split ( & actor, Some ( reducer_typehash) ) ;
3009
+
3010
+ // Send 9 messages.
3011
+ for msg in [ 1 , 5 , 3 , 4 , 2 , 91 , 92 , 93 , 94 ] {
3012
+ post ( & actor, split_port_id. clone ( ) , msg) ;
3013
+ }
3014
+ // The first 5 should be batched and reduced once due
3015
+ // to every_n_msgs = 5.
3016
+ let messages = wait_for ( & mut receiver, 1 , Duration :: from_secs ( 2 ) )
3017
+ . await
3018
+ . unwrap ( ) ;
3019
+ assert_eq ! ( messages, vec![ 15 ] ) ;
3020
+
3021
+ // the last message unfortranately will never come because they do not
3022
+ // reach batch size.
3023
+ RealClock . sleep ( Duration :: from_secs ( 2 ) ) . await ;
3024
+ let msg = receiver. try_recv ( ) . unwrap ( ) ;
3025
+ assert_eq ! ( msg, None ) ;
3026
+ }
2968
3027
}
0 commit comments