|
1 | 1 | use dkn_p2p::libp2p::{multiaddr::Protocol, Multiaddr, PeerId};
|
2 | 2 | use dkn_utils::{DriaNetwork, SemanticVersion};
|
3 | 3 | use eyre::{Context, OptionExt, Result};
|
| 4 | +use rand::seq::SliceRandom; |
4 | 5 | use std::fmt::Debug;
|
5 | 6 |
|
6 | 7 | /// The connected RPC node, as per the Star network topology.
|
@@ -43,23 +44,46 @@ async fn get_rpc_for_network(
|
43 | 44 | network: &DriaNetwork,
|
44 | 45 | version: &SemanticVersion,
|
45 | 46 | ) -> Result<Multiaddr> {
|
| 47 | + const MIN_MARGIN: usize = 150; |
| 48 | + |
46 | 49 | let response = reqwest::get(network.discovery_url(version)).await?;
|
47 | 50 | let rpcs_and_peer_counts = response
|
48 | 51 | .json::<Vec<(Multiaddr, usize)>>()
|
49 | 52 | .await
|
50 | 53 | .wrap_err("could not parse API response")?;
|
51 | 54 |
|
52 |
| - // returns the RPC address with the least peer count (for load balancing) |
53 |
| - rpcs_and_peer_counts |
| 55 | + // ensure that the response contains at least one RPC |
| 56 | + if rpcs_and_peer_counts.is_empty() { |
| 57 | + eyre::bail!("no RPCs were returned by discovery API"); |
| 58 | + } |
| 59 | + |
| 60 | + // get the minimum count of peers from all RPCs |
| 61 | + let min_peer_count = rpcs_and_peer_counts |
| 62 | + .iter() |
| 63 | + .map(|(_, peer_count)| *peer_count) |
| 64 | + .min() |
| 65 | + .unwrap(); // safe to unwrap because we checked for empty earlier |
| 66 | + |
| 67 | + // choose the RPCs that have peers in range `[min_peer_count, min_peer_count + MIN_MARGIN]` |
| 68 | + let rpcs_and_peer_counts: Vec<(Multiaddr, usize)> = rpcs_and_peer_counts |
54 | 69 | .into_iter()
|
55 |
| - .min_by_key(|(_, peer_count)| *peer_count) |
56 |
| - .ok_or_eyre("no RPCs were returned by discovery API") |
| 70 | + .filter(|(_, peer_count)| { |
| 71 | + (min_peer_count..=min_peer_count + MIN_MARGIN).contains(peer_count) |
| 72 | + }) |
| 73 | + .collect(); |
| 74 | + |
| 75 | + // pick a random RPC from the filtered list |
| 76 | + let chosen_rpc = rpcs_and_peer_counts |
| 77 | + .choose(&mut rand::thread_rng()) |
| 78 | + .cloned() |
57 | 79 | .map(|(addr, _)| addr)
|
| 80 | + .unwrap(); // safe to unwrap because we checked for empty earlier |
| 81 | + |
| 82 | + Ok(chosen_rpc) |
58 | 83 | }
|
59 | 84 |
|
60 | 85 | #[cfg(test)]
|
61 | 86 | mod tests {
|
62 |
| - |
63 | 87 | use super::*;
|
64 | 88 |
|
65 | 89 | #[tokio::test]
|
|
0 commit comments