@@ -38,6 +38,7 @@ use crate::model::SharedRunContext;
3838const REQUEST_BACKOFF_INITIAL : Duration = Duration :: from_secs ( 1 ) ;
3939const REQUEST_BACKOFF_MAX : Duration = Duration :: from_secs ( 8 ) ;
4040const HOUSEKEEPING_INTERVAL : Duration = Duration :: from_millis ( 400 ) ;
41+ const RESEND_MIN_INTERVAL : Duration = Duration :: from_secs ( 2 ) ;
4142#[ cfg( not( test) ) ]
4243const SYNC_REPLY_DEBOUNCE : Duration = Duration :: from_secs ( 2 ) ;
4344#[ cfg( test) ]
@@ -847,7 +848,11 @@ impl DisseminationManager {
847848 return Ok ( false ) ;
848849 }
849850
850- let filtered_targets = candidate_targets;
851+ let now = TimestampMsClock :: now ( ) ;
852+ let filtered_targets: HashSet < ValidatorPublicKey > = candidate_targets
853+ . into_iter ( )
854+ . filter ( |peer| self . can_send_to_peer ( lane_id, dp_hash, peer, now. clone ( ) ) )
855+ . collect ( ) ;
851856 if filtered_targets. is_empty ( ) {
852857 return Ok ( false ) ;
853858 }
@@ -991,6 +996,33 @@ impl DisseminationManager {
991996 . insert ( ( lane_id. clone ( ) , dp_hash. clone ( ) ) , now) ;
992997 }
993998
999+ fn can_send_to_peer (
1000+ & self ,
1001+ lane_id : & LaneId ,
1002+ dp_hash : & DataProposalHash ,
1003+ peer : & ValidatorPublicKey ,
1004+ now : TimestampMs ,
1005+ ) -> bool {
1006+ let key = ( lane_id. clone ( ) , dp_hash. clone ( ) , peer. clone ( ) ) ;
1007+ let evidence = self . knowledge . by_dp . get ( & key) ;
1008+ let should_rate_limit = matches ! (
1009+ evidence,
1010+ Some ( EvidenceState :: WeakHas )
1011+ | Some ( EvidenceState :: StrongHas )
1012+ | Some ( EvidenceState :: ObservedHas )
1013+ ) ;
1014+
1015+ if !should_rate_limit {
1016+ return true ;
1017+ }
1018+
1019+ self . knowledge
1020+ . by_peer
1021+ . get ( peer)
1022+ . and_then ( |state| state. last_dp_sent . get ( & ( lane_id. clone ( ) , dp_hash. clone ( ) ) ) )
1023+ . is_none_or ( |last_sent| now - last_sent. clone ( ) >= RESEND_MIN_INTERVAL )
1024+ }
1025+
9941026 fn should_debounce_sync_reply (
9951027 & self ,
9961028 lane_id : & LaneId ,
@@ -1010,6 +1042,19 @@ impl DisseminationManager {
10101042 self . owned_lanes . insert ( lane_id) ;
10111043 }
10121044
1045+ #[ cfg( test) ]
1046+ pub ( crate ) fn clear_last_dp_sent_for_test (
1047+ & mut self ,
1048+ lane_id : & LaneId ,
1049+ dp_hash : & DataProposalHash ,
1050+ ) {
1051+ for peer_state in self . knowledge . by_peer . values_mut ( ) {
1052+ peer_state
1053+ . last_dp_sent
1054+ . remove ( & ( lane_id. clone ( ) , dp_hash. clone ( ) ) ) ;
1055+ }
1056+ }
1057+
10131058 #[ cfg( test) ]
10141059 pub ( crate ) async fn process_sync_requests_and_replies_for_test ( & mut self ) -> Result < ( ) > {
10151060 self . process_sync_requests_and_replies ( ) . await
0 commit comments