use std::time::Duration;
use super::Interval;
pub fn fibonacci(base: Duration, max: Duration) -> Interval {
fn get_unit_fibonacci_seq(max_ratio: u32) -> Vec<u32> {
let mut prev = 1;
let mut curr = 1;
let mut ans = vec![curr];
loop {
let new_value = prev + curr;
if new_value <= max_ratio {
ans.push(new_value);
prev = curr;
curr = new_value;
} else {
break ans;
}
}
}
if base == Duration::new(0, 0) {
panic!("base duration can not set to zero");
}
if max < base {
panic!("max duration must greater than or equal with base duration");
}
let max_ratio = (max.as_secs_f64() / base.as_secs_f64()) as u32;
let unit_fibonacci_seq = get_unit_fibonacci_seq(max_ratio);
let len = unit_fibonacci_seq.len();
Interval::new(
move |log| {
let failure_count = log.unwrap().failure_count();
if (0..len).contains(&failure_count) {
base * unit_fibonacci_seq[failure_count]
} else {
base * *unit_fibonacci_seq.last().unwrap_or(&0)
}
},
len,
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::throttle::*;
#[test]
#[should_panic]
fn test_fibonacci_max_smaller_than_base() {
fibonacci(Duration::from_millis(10), Duration::from_millis(5));
}
#[test]
fn test_fibonacci() {
fn fibonacci_test_case(base_ms: u64, max_ms: u64, delay_ms: Vec<u64>) {
fn get_sample_throttle_log(failure_count: usize, total: usize) -> ThrottleLog {
let mut log = ThrottleLog::new(total);
for _ in 0..failure_count {
log.push(LogRecord {
time: Instant::now(),
successful: false,
})
}
log
}
let algo = fibonacci(
Duration::from_millis(base_ms),
Duration::from_millis(max_ms),
);
for (f_count, ms) in delay_ms.iter().enumerate() {
let throttle_log = get_sample_throttle_log(f_count, algo.log_size);
assert_eq!(
(algo.interval_fn)(Some(&throttle_log)),
Duration::from_millis(*ms)
);
}
}
fibonacci_test_case(10, 10, vec![10, 10, 10]);
fibonacci_test_case(10, 15, vec![10, 10, 10]);
fibonacci_test_case(10, 20, vec![10, 20, 20, 20]);
fibonacci_test_case(10, 60, vec![10, 20, 30, 50, 50, 50]);
}
}