use super::{SelectionContext, Strategy};
use crate::LoadMetric;
#[derive(Debug)]
pub struct LeastLoad;
impl LeastLoad {
pub fn new() -> Self {
Self
}
}
impl Default for LeastLoad {
fn default() -> Self {
Self
}
}
impl<N: LoadMetric> Strategy<N> for LeastLoad {
fn select(&self, candidates: &[N], ctx: &SelectionContext) -> Option<usize> {
if candidates.is_empty() {
return None;
}
let mut best_idx = None;
let mut best_score = f64::INFINITY;
for (i, node) in candidates.iter().enumerate() {
if ctx.is_excluded(i) {
continue;
}
let score = node.load_score();
if score < best_score {
best_score = score;
best_idx = Some(i);
}
}
best_idx
}
}
#[cfg(test)]
mod tests {
use super::*;
struct L(f64);
impl LoadMetric for L {
fn load_score(&self) -> f64 {
self.0
}
}
#[test]
fn picks_lowest_load() {
let ll = LeastLoad::new();
let nodes = [L(3.0), L(1.0), L(2.0)];
assert_eq!(ll.select(&nodes, &SelectionContext::default()), Some(1));
}
#[test]
fn picks_first_on_tie() {
let ll = LeastLoad::new();
let nodes = [L(1.0), L(1.0), L(2.0)];
assert_eq!(ll.select(&nodes, &SelectionContext::default()), Some(0));
}
#[test]
fn skips_excluded_picks_next_best() {
let ll = LeastLoad::new();
let nodes = [L(1.0), L(2.0), L(3.0)];
let ctx = SelectionContext::builder().exclude(vec![0]).build();
assert_eq!(ll.select(&nodes, &ctx), Some(1));
}
}