fix(probe): retain reflector set when too few candidates respond
Build / build (pull_request) Failing after 5s
DevSkim / DevSkim (pull_request) Failing after 1m20s
rust-clippy analyze / Run rust-clippy analyzing (pull_request) Failing after 1m41s

This commit is contained in:
Nils Andreas Svee committed 2026-09-24 23:49:39 +07:00
1 parent bcea31aac4
commit 5a89cc0d9e
1 file changed
+67 -10
+67 -10
View File
@@ -229,10 +229,20 @@ impl ReflectorSelector {
candidates.sort_by(|a, b| a.1.cmp(&b.1)); candidates.sort_by(|a, b| a.1.cmp(&b.1));
// Now we will just limit the candidates down to 2 * num_reflectors // Now we will just limit the candidates down to 2 * num_reflectors
let mut num_reflectors = self.settings.advanced_settings.num_reflectors; let num_reflectors = self.settings.advanced_settings.num_reflectors;
let candidate_pool_num = (2 * num_reflectors) as usize; let candidate_pool_num = (2 * num_reflectors) as usize;
candidates.truncate(candidate_pool_num); candidates.truncate(candidate_pool_num);
if candidates.len() < num_reflectors as usize {
log::warn!(
"Reselection found {}/{} responsive peers; retaining the current peers",
candidates.len(),
num_reflectors
);
*reflectors_peers = previous_peers;
continue;
}
for (candidate, rtt) in candidates.iter() { for (candidate, rtt) in candidates.iter() {
info!("Fastest candidate {}: {}", candidate, rtt); info!("Fastest candidate {}: {}", candidate, rtt);
} }
@@ -243,10 +253,6 @@ impl ReflectorSelector {
candidates.swap(i, j); candidates.swap(i, j);
} }
if (candidates.len() as u8) < num_reflectors {
num_reflectors = candidates.len() as u8;
}
let mut new_peers = Vec::new(); let mut new_peers = Vec::new();
for i in 0..num_reflectors { for i in 0..num_reflectors {
let peer = candidates[i as usize].0; let peer = candidates[i as usize].0;
@@ -260,11 +266,6 @@ impl ReflectorSelector {
}); });
} }
if new_peers.is_empty() {
log::warn!("Reselection found no responsive peers; retaining the current peers");
*reflectors_peers = previous_peers;
continue;
}
*reflectors_peers = new_peers; *reflectors_peers = new_peers;
} }
} }
@@ -274,6 +275,9 @@ impl ReflectorSelector {
mod tests { mod tests {
use super::*; use super::*;
use crate::baseliner::{EwmaStats, ReflectorState}; use crate::baseliner::{EwmaStats, ReflectorState};
use crate::settings::{
AdvancedSettings, NetworkSettings, ObservabilitySettings, OutputSettings,
};
use std::collections::HashMap; use std::collections::HashMap;
use std::thread; use std::thread;
@@ -424,4 +428,57 @@ mod tests {
None None
); );
} }
#[test]
fn reselection_keeps_peers_when_fewer_than_configured_respond() {
let previous_peers: Vec<IpAddr> = (1..=5)
.map(|last| IpAddr::from([192, 0, 2, last]))
.collect();
let candidate = IpAddr::from([192, 0, 2, 6]);
let peers = Arc::new(RwLock::new(previous_peers.clone()));
let (trigger_tx, trigger_rx) = flume::unbounded();
let (snapshot_tx, snapshot_rx) = flume::unbounded();
trigger_tx.send(true).unwrap();
drop(trigger_tx);
let producer_peers = Arc::clone(&peers);
let producer = thread::spawn(move || {
let deadline = Instant::now() + Duration::from_secs(1);
while !producer_peers.read().unwrap().contains(&candidate) {
assert!(Instant::now() < deadline);
sleep(Duration::from_millis(1));
}
snapshot_tx
.send(snapshot_with_rtt(Instant::now(), candidate, 20.0))
.unwrap();
});
let selector = ReflectorSelector {
settings: Settings {
network: NetworkSettings {
download_interface: "ifb0".into(),
upload_interface: "eth0".into(),
download_base_kbits: 10_000.0,
download_min_percent: 20.0,
upload_base_kbits: 5_000.0,
upload_min_percent: 20.0,
},
output: OutputSettings::default(),
observability: ObservabilitySettings::default(),
advanced_settings: AdvancedSettings {
tick_interval: 0.1,
..AdvancedSettings::default()
},
},
snapshot_rx,
reflector_peers_lock: Arc::clone(&peers),
reflector_pool: vec![candidate],
trigger_channel: trigger_rx,
metrics: MetricsSender::disabled(),
};
selector.run().unwrap();
producer.join().unwrap();
assert_eq!(*peers.read().unwrap(), previous_peers);
}
} }