8383 peer_selection_config : PeerSelectionConfig ,
8484 /// Global stats
8585 global_stats : GlobalStats ,
86+ /// Track the first Have response time for each CID (for wait window)
87+ cid_first_have_time : FnvHashMap < CidGeneric < S > , Instant > ,
8688}
8789
8890#[ derive( Debug ) ]
@@ -158,6 +160,7 @@ where
158160 new_blocks : Vec :: new ( ) ,
159161 peer_selection_config : PeerSelectionConfig :: default ( ) ,
160162 global_stats : GlobalStats :: default ( ) ,
163+ cid_first_have_time : FnvHashMap :: default ( ) ,
161164 }
162165 }
163166
@@ -276,6 +279,7 @@ where
276279 let cid = cid. to_owned ( ) ;
277280 self . cid_to_queries . remove ( & cid) ;
278281 self . wantlist . remove ( & cid) ;
282+ self . cid_first_have_time . remove ( & cid) ;
279283 }
280284
281285 break ;
@@ -290,10 +294,14 @@ where
290294
291295 let mut new_blocks = Vec :: new ( ) ;
292296
293- // Update presence
297+ // Update presence and track first Have time for wait window
294298 for ( cid, block_presence) in msg. block_presences {
295299 match block_presence {
296- BlockPresenceType :: Have => peer_state. wantlist . got_have ( & cid) ,
300+ BlockPresenceType :: Have => {
301+ peer_state. wantlist . got_have ( & cid) ;
302+ // Record first Have time for this CID (for wait window mechanism)
303+ self . cid_first_have_time . entry ( cid) . or_insert_with ( Instant :: now) ;
304+ }
297305 BlockPresenceType :: DontHave => peer_state. wantlist . got_dont_have ( & cid) ,
298306 }
299307 }
@@ -340,6 +348,9 @@ where
340348 continue ;
341349 }
342350
351+ // Clean up wait window tracking
352+ self . cid_first_have_time . remove ( & cid) ;
353+
343354 peer_state. wantlist . got_block ( & cid) ;
344355 new_blocks. push ( ( cid, block. clone ( ) ) ) ;
345356
@@ -392,8 +403,11 @@ where
392403 }
393404 }
394405
395- // Select optimal peers for each CID
406+ // Select optimal peers for each CID (with wait window mechanism)
396407 let mut selected_peers_for_cid: FnvHashMap < CidGeneric < S > , Vec < PeerId > > = FnvHashMap :: default ( ) ;
408+ let total_peers = self . peers . len ( ) ;
409+ let wait_window = Duration :: from_millis ( self . peer_selection_config . have_wait_window_ms ) ;
410+ let min_candidate_ratio = self . peer_selection_config . min_candidate_ratio ;
397411
398412 for ( cid, candidate_peers) in & cid_to_candidates {
399413 let already_sent = * cid_already_sent_count. get ( cid) . unwrap_or ( & 0 ) ;
@@ -404,6 +418,37 @@ where
404418 continue ;
405419 }
406420
421+ // Wait window check: ensure we have enough candidates before selecting
422+ let should_select = if let Some ( first_have_time) = self . cid_first_have_time . get ( cid) {
423+ let elapsed = first_have_time. elapsed ( ) ;
424+ let min_candidates = ( ( total_peers as f64 ) * min_candidate_ratio) . ceil ( ) as usize ;
425+
426+ // Start selection if:
427+ // 1. Wait window has elapsed, OR
428+ // 2. We have enough candidates (>= min_candidate_ratio), OR
429+ // 3. We have enough candidates to fill remaining slots
430+ let window_elapsed = elapsed >= wait_window;
431+ let enough_candidates = candidate_peers. len ( ) >= min_candidates;
432+ let can_fill_slots = candidate_peers. len ( ) >= ( top_n - already_sent) ;
433+
434+ if !window_elapsed && !enough_candidates && !can_fill_slots {
435+ debug ! (
436+ "CID {} - waiting for more candidates: elapsed={:?}, candidates={}, min={}, window={:?}" ,
437+ cid, elapsed, candidate_peers. len( ) , min_candidates, wait_window
438+ ) ;
439+ false
440+ } else {
441+ true
442+ }
443+ } else {
444+ // No first Have time recorded yet (shouldn't happen, but be safe)
445+ true
446+ } ;
447+
448+ if !should_select {
449+ continue ;
450+ }
451+
407452 let need_to_select = top_n - already_sent;
408453
409454 let candidates_with_metrics: FnvHashMap < PeerId , & PeerMetrics > = candidate_peers
@@ -422,8 +467,9 @@ where
422467 let selected = PeerSelector :: select_top_peers ( & candidates_with_metrics, & temp_config) ;
423468
424469 debug ! (
425- "CID {} - sent: {}, candidates: {}, selected: {} (top_n={})" ,
426- cid, already_sent, candidates_with_metrics. len( ) , selected. len( ) , top_n
470+ "CID {} - sent: {}, candidates: {}, selected: {} (top_n={}, waited: {:?})" ,
471+ cid, already_sent, candidates_with_metrics. len( ) , selected. len( ) , top_n,
472+ self . cid_first_have_time. get( cid) . map( |t| t. elapsed( ) )
427473 ) ;
428474
429475 selected_peers_for_cid. insert ( * cid, selected) ;
0 commit comments