fix: lb random range bug

This commit is contained in:
Jun Kurihara 2023-06-07 15:03:06 +09:00
commit f4c59c9f2f
No known key found for this signature in database
GPG key ID: 48ADFD173ED22B03
2 changed files with 62 additions and 37 deletions

View file

@ -1,4 +1,5 @@
use derive_builder::Builder; use derive_builder::Builder;
use rand::Rng;
use std::sync::{ use std::sync::{
atomic::{AtomicUsize, Ordering}, atomic::{AtomicUsize, Ordering},
Arc, Arc,
@ -13,50 +14,85 @@ pub(super) mod load_balance_options {
} }
#[derive(Debug, Clone, Builder)] #[derive(Debug, Clone, Builder)]
/// Counter object as a pointer to the current serving upstream destination /// Round Robin LB object as a pointer to the current serving upstream destination
pub struct LbRoundRobinCount { pub struct LbRoundRobin {
#[builder(default)] #[builder(default)]
cnt: Arc<AtomicUsize>, /// Pointer to the index of the last served upstream destination
ptr: Arc<AtomicUsize>,
#[builder(setter(custom), default)] #[builder(setter(custom), default)]
max_val: usize, /// Number of upstream destinations
num_upstreams: usize,
} }
impl LbRoundRobinCountBuilder { impl LbRoundRobinBuilder {
pub fn max_val(&mut self, v: &usize) -> &mut Self { pub fn num_upstreams(&mut self, v: &usize) -> &mut Self {
self.max_val = Some(*v); self.num_upstreams = Some(*v);
self self
} }
} }
impl LbRoundRobinCount { impl LbRoundRobin {
/// Get a current count of upstream served /// Get a current count of upstream served
fn current_cnt(&self) -> usize { fn current_ptr(&self) -> usize {
self.cnt.load(Ordering::Relaxed) self.ptr.load(Ordering::Relaxed)
} }
/// Increment the count of upstream served up to the max value /// Increment the count of upstream served up to the max value
pub fn increment_cnt(&self) -> usize { pub fn increment_ptr(&self) -> usize {
if self.current_cnt() < self.max_val - 1 { if self.current_ptr() < self.num_upstreams - 1 {
self.cnt.fetch_add(1, Ordering::Relaxed) self.ptr.fetch_add(1, Ordering::Relaxed)
} else { } else {
// Clear the counter // Clear the counter
self.cnt.fetch_and(0, Ordering::Relaxed) self.ptr.fetch_and(0, Ordering::Relaxed)
} }
} }
} }
#[derive(Debug, Clone, Builder)]
/// Random LB object to keep the object of random pools
pub struct LbRandom {
#[builder(setter(custom), default)]
/// Number of upstream destinations
num_upstreams: usize,
}
impl LbRandomBuilder {
pub fn num_upstreams(&mut self, v: &usize) -> &mut Self {
self.num_upstreams = Some(*v);
self
}
}
impl LbRandom {
/// Returns the random index within the range
pub fn get_ptr(&self) -> usize {
let mut rng = rand::thread_rng();
rng.gen_range(0..self.num_upstreams)
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Load Balancing Option /// Load Balancing Option
pub enum LoadBalance { pub enum LoadBalance {
/// Fix to the first upstream. Use if only one upstream destination is specified /// Fix to the first upstream. Use if only one upstream destination is specified
FixToFirst, FixToFirst,
/// Randomly chose one upstream server /// Randomly chose one upstream server
Random, Random(LbRandom),
/// Simple round robin without session persistance /// Simple round robin without session persistance
RoundRobin(LbRoundRobinCount), RoundRobin(LbRoundRobin),
/// Round robin with session persistance using cookie /// Round robin with session persistance using cookie
StickyRoundRobin(LbRoundRobinCount), StickyRoundRobin(LbRoundRobin),
} }
impl Default for LoadBalance { impl Default for LoadBalance {
fn default() -> Self { fn default() -> Self {
Self::FixToFirst Self::FixToFirst
} }
} }
impl LoadBalance {
/// Get the index of the upstream serving the incoming request
pub(super) fn get_idx(&self) -> usize {
match self {
LoadBalance::FixToFirst => 0usize,
LoadBalance::RoundRobin(ptr) => ptr.increment_ptr(),
LoadBalance::Random(v) => v.get_ptr(),
LoadBalance::StickyRoundRobin(_ptr) => 0usize, // todo!(), // TODO: TODO: TODO: TODO: tentative value
}
}
}

View file

@ -1,10 +1,9 @@
use super::{ use super::{
load_balance::{load_balance_options as lb_opts, LbRoundRobinCountBuilder, LoadBalance}, load_balance::{load_balance_options as lb_opts, LbRandomBuilder, LbRoundRobinBuilder, LoadBalance},
BytesName, PathNameBytesExp, UpstreamOption, BytesName, PathNameBytesExp, UpstreamOption,
}; };
use crate::log::*; use crate::log::*;
use derive_builder::Builder; use derive_builder::Builder;
use rand::Rng;
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
use std::borrow::Cow; use std::borrow::Cow;
@ -96,16 +95,16 @@ impl UpstreamGroupBuilder {
let lb = if let Some(x) = v { let lb = if let Some(x) = v {
match x.as_str() { match x.as_str() {
lb_opts::FIX_TO_FIRST => LoadBalance::FixToFirst, lb_opts::FIX_TO_FIRST => LoadBalance::FixToFirst,
lb_opts::RANDOM => LoadBalance::Random, lb_opts::RANDOM => LoadBalance::Random(LbRandomBuilder::default().num_upstreams(upstream_num).build().unwrap()),
lb_opts::ROUND_ROBIN => LoadBalance::RoundRobin( lb_opts::ROUND_ROBIN => LoadBalance::RoundRobin(
LbRoundRobinCountBuilder::default() LbRoundRobinBuilder::default()
.max_val(upstream_num) .num_upstreams(upstream_num)
.build() .build()
.unwrap(), .unwrap(),
), ),
lb_opts::STICKY_ROUND_ROBIN => LoadBalance::StickyRoundRobin( lb_opts::STICKY_ROUND_ROBIN => LoadBalance::StickyRoundRobin(
LbRoundRobinCountBuilder::default() LbRoundRobinBuilder::default()
.max_val(upstream_num) .num_upstreams(upstream_num)
.build() .build()
.unwrap(), .unwrap(),
), ),
@ -137,18 +136,8 @@ impl UpstreamGroupBuilder {
impl UpstreamGroup { impl UpstreamGroup {
/// Get an enabled option of load balancing [[LoadBalance]] /// Get an enabled option of load balancing [[LoadBalance]]
pub fn get(&self) -> Option<&Upstream> { pub fn get(&self) -> Option<&Upstream> {
match &self.lb { let idx = self.lb.get_idx();
LoadBalance::FixToFirst => self.upstream.get(0), debug!("Upstream of index {idx} is chosen.");
LoadBalance::RoundRobin(cnt) => { self.upstream.get(idx)
let idx = cnt.increment_cnt();
self.upstream.get(idx)
}
LoadBalance::Random => {
let mut rng = rand::thread_rng();
let max = self.upstream.len() - 1;
self.upstream.get(rng.gen_range(0..max))
}
LoadBalance::StickyRoundRobin(_cnt) => todo!(), // TODO: TODO:
}
} }
} }