@@ -363,23 +363,71 @@ impl WorkersBroadcaster for NonblockingBroadcaster {
363363 }
364364}
365365
366- /// Extracts a list of unique leader addresses to which transactions will be sent.
366+ /// Extracts leaders to send transactions to, up to `send_fanout` count.
367+ ///
368+ /// In Solana's leader schedule, each leader produces blocks for [`solana_clock::NUM_CONSECUTIVE_LEADER_SLOTS`]
369+ /// consecutive slots. The schedule might look like: [L1, L1, L2, L3], where each entry
370+ /// represents a leader producing [`solana_clock::NUM_CONSECUTIVE_LEADER_SLOTS`] slots.
371+ /// So L1, L1 means L1 produces 2 * [`solana_clock::NUM_CONSECUTIVE_LEADER_SLOTS`] consecutive slots.
372+ ///
373+ /// This function skips consecutive duplicates because sending the same transaction multiple
374+ /// times to the same leader during their consecutive slot range is redundant.
375+ ///
376+ /// However, non-consecutive duplicates (e.g., L1 appears again after L2) are preserved because
377+ /// they represent legitimate retry opportunities at different time windows.
378+ ///
379+ /// # Examples
380+ ///
381+ /// [L1, L1, L2] with fanout=3 -> [L1, L2]
382+ /// L1 has 2 * NUM_CONSECUTIVE_LEADER_SLOTS consecutive slots, no need to send twice
367383///
368- /// This function selects up to `send_fanout` addresses from the `leaders` list, ensuring that
369- /// only unique addresses are included while maintaining their original order.
370- fn extract_send_leaders ( leaders : & [ SocketAddr ] , send_fanout : usize ) -> Vec < SocketAddr > {
371- let send_count = send_fanout. min ( leaders. len ( ) ) ;
372- remove_duplicates ( & leaders[ ..send_count] )
373- }
374-
375- /// Removes duplicate `SocketAddr` elements from the given slice while
376- /// preserving their original order.
377- fn remove_duplicates ( input : & [ SocketAddr ] ) -> Vec < SocketAddr > {
378- let mut res = Vec :: with_capacity ( input. len ( ) ) ;
379- for address in input {
380- if !res. contains ( address) {
381- res. push ( * address) ;
384+ /// [L1, L2, L1] with fanout=3 -> [L1, L2, L1]
385+ /// L1 appears at different times, worth retrying
386+ pub fn extract_send_leaders ( leaders : & [ SocketAddr ] , send_fanout : usize ) -> Vec < SocketAddr > {
387+ let mut result = Vec :: with_capacity ( send_fanout) ;
388+ let mut prev_leader: Option < SocketAddr > = None ;
389+
390+ for & leader in leaders {
391+ // Skip only consecutive duplicates
392+ if prev_leader != Some ( leader) {
393+ result. push ( leader) ;
394+ if result. len ( ) >= send_fanout {
395+ break ;
396+ }
382397 }
398+ prev_leader = Some ( leader) ;
383399 }
384- res
400+
401+ result
385402}
403+
404+ #[ cfg( test) ]
405+ mod tests {
406+ use super :: * ;
407+ use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
408+
409+ fn make_addr ( port : u16 ) -> SocketAddr {
410+ SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) ) , port)
411+ }
412+
413+ #[ test]
414+ fn test_consecutive_duplicates_skipped ( ) {
415+ let l1 = make_addr ( 1001 ) ;
416+ let l2 = make_addr ( 1002 ) ;
417+ let l3 = make_addr ( 1003 ) ;
418+
419+ // Consecutive duplicates should be skipped
420+ let leaders = vec ! [ l1, l1, l1, l2, l2, l3] ;
421+ assert_eq ! ( extract_send_leaders( & leaders, 3 ) , vec![ l1, l2, l3] ) ;
422+ }
423+
424+ #[ test]
425+ fn test_non_consecutive_duplicates_kept ( ) {
426+ let l1 = make_addr ( 1001 ) ;
427+ let l2 = make_addr ( 1002 ) ;
428+
429+ // Non-consecutive duplicates are legitimate retries
430+ let leaders = vec ! [ l1, l2, l1] ;
431+ assert_eq ! ( extract_send_leaders( & leaders, 3 ) , vec![ l1, l2, l1] ) ;
432+ }
433+ }
0 commit comments