From e8be8da4b62485adb1aea84787425c92986c9176 Mon Sep 17 00:00:00 2001 From: Esteve Soler Arderiu Date: Mon, 20 Apr 2026 13:32:02 +0200 Subject: [PATCH] perf(p2p): randomize contact selection for RLPx connection initiation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace sequential bucket traversal (0→255) in get_contact_to_initiate with random selection across all eligible contacts. The sequential approach was biased toward sparse low-numbered buckets, wasting connection attempts before reaching the dense high-numbered buckets (253-255) where ~87% of contacts cluster due to XOR distance distribution. --- crates/networking/p2p/peer_table.rs | 35 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/crates/networking/p2p/peer_table.rs b/crates/networking/p2p/peer_table.rs index 78a91f05bc..9dee1c1e27 100644 --- a/crates/networking/p2p/peer_table.rs +++ b/crates/networking/p2p/peer_table.rs @@ -1055,24 +1055,31 @@ impl PeerTableServer { } fn do_get_contact_to_initiate(&mut self) -> Option { - // Check both main contacts and replacements in each bucket. - // Replacements may contain fresher peers that haven't been tried yet. - for bucket in &self.buckets { - for (node_id, contact) in bucket.contacts.iter().chain(bucket.replacements.iter()) { - if !self.peers.contains_key(node_id) - && !self.already_tried_peers.contains(node_id) + // Collect all eligible contacts across all buckets (main + replacements) + // and pick one randomly to avoid sequential bias toward sparse low-numbered + // buckets. Most contacts cluster in high-numbered buckets (253-255) due to + // XOR distance distribution, so sequential traversal would waste attempts + // on the sparse lower buckets before reaching them. + let eligible: Vec<(H256, Contact)> = self + .iter_contacts() + .filter(|(node_id, contact)| { + !self.peers.contains_key(*node_id) + && !self.already_tried_peers.contains(*node_id) && contact.knows_us && !contact.unwanted && contact.is_fork_id_valid != Some(false) - { - self.already_tried_peers.insert(*node_id); - return Some(contact.clone()); - } - } + }) + .map(|(id, c)| (*id, c.clone())) + .collect(); + + if let Some((node_id, contact)) = eligible.choose(&mut rand::rngs::OsRng).cloned() { + self.already_tried_peers.insert(node_id); + Some(contact) + } else { + tracing::trace!("Resetting list of tried peers."); + self.already_tried_peers.clear(); + None } - tracing::trace!("Resetting list of tried peers."); - self.already_tried_peers.clear(); - None } fn do_get_contact_for_lookup(&self, protocol: DiscoveryProtocol) -> Option {