mod strategy;
use {
crate::{
Error,
Result,
Service,
},
strategy::Strategy,
};
#[derive(Default)]
pub struct Balancer(Strategy);
impl Balancer {
pub(super) fn new(strategy: &str) -> Self { Self(strategy.into()) }
pub fn select<'a>(
&'a mut self,
instances: &'a [Box<dyn Service>],
) -> Result<&'a Box<dyn Service>> {
if instances.is_empty() {
return Err(Error::ServiceNotFound);
}
match &mut self.0 {
Strategy::RoundRobin { index } => {
let service = &instances[*index % instances.len()];
*index += 1;
Ok(service)
}
Strategy::Weighted { index, .. } => {
let service = &instances[*index % instances.len()];
*index += 1;
Ok(service)
}
Strategy::LeastConnections { .. } => {
Ok(&instances[0])
}
Strategy::Random => {
Ok(&instances[0])
}
}
}
pub fn strategy(&self) -> &str { self.0.as_ref() }
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::{
Address,
Envelope,
Reply,
Result,
},
};
#[derive(Debug, Clone)]
struct NoService(Address);
impl Service for NoService {
fn address(&self) -> &Address { &self.0 }
fn duplicate(&self) -> Box<dyn Service> { Box::new(self.clone()) }
fn process(&self, _message: Envelope) -> Result<Reply> { Ok(None) }
}
#[test]
fn default_balancer() {
let balancer = Balancer::default();
let round_robin = Strategy::RoundRobin { index: 0 };
assert_eq!(balancer.0, round_robin);
assert_eq!(balancer.strategy(), "round_robin");
}
#[test]
fn new_balancer() {
let strategy = "random";
let balancer = Balancer::new(strategy);
assert_eq!(balancer.0, Strategy::Random);
assert_eq!(balancer.strategy(), "random");
}
#[test]
fn balancer_select() -> Result<()> {
let src1 = "http://no-service-1.com";
let src2 = "http://no-service-2.com";
let src3 = "http://no-service-3.com";
let addr1 = Address::parse(src1)?;
let addr2 = Address::parse(src2)?;
let addr3 = Address::parse(src3)?;
let srv1 = NoService(addr1);
let srv2 = NoService(addr2);
let srv3 = NoService(addr3);
let instances: Vec<Box<dyn Service>> = vec![Box::new(srv1), Box::new(srv2), Box::new(srv3)];
let mut balancer = Balancer::default();
let selected = balancer.select(&instances)?;
assert_eq!(selected.address().to_string(), src1);
let selected = balancer.select(&instances)?;
assert_eq!(selected.address().to_string(), src2);
let selected = balancer.select(&instances)?;
assert_eq!(selected.address().to_string(), src3);
let selected = balancer.select(&instances)?;
assert_eq!(selected.address().to_string(), src1);
Ok(())
}
#[test]
fn balancer_select_on_empty_list() {
let instances: Vec<Box<dyn Service>> = vec![];
let mut balancer = Balancer::default();
let result = balancer.select(&instances);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
Error::ServiceNotFound
))
}
}