Skip to content

Commit

Permalink
feat: builder_without_max_(times|delay) (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
nardoor authored Nov 22, 2024
1 parent 073c744 commit 0e14575
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 14 deletions.
45 changes: 32 additions & 13 deletions backon/src/backoff/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ impl ConstantBuilder {
self.jitter = true;
self
}

/// Set no max times for the backoff.
///
/// The backoff will not stop by itself.
///
/// _The backoff could stop reaching `usize::MAX` attempts but this is **unrealistic**._
pub fn without_max_times(mut self) -> Self {
self.max_times = None;
self
}
}

impl BackoffBuilder for ConstantBuilder {
Expand Down Expand Up @@ -139,32 +149,32 @@ mod tests {

#[test]
fn test_constant_default() {
let mut exp = ConstantBuilder::default().build();
let mut it = ConstantBuilder::default().build();

assert_eq!(Some(Duration::from_secs(1)), exp.next());
assert_eq!(Some(Duration::from_secs(1)), exp.next());
assert_eq!(Some(Duration::from_secs(1)), exp.next());
assert_eq!(None, exp.next());
assert_eq!(Some(Duration::from_secs(1)), it.next());
assert_eq!(Some(Duration::from_secs(1)), it.next());
assert_eq!(Some(Duration::from_secs(1)), it.next());
assert_eq!(None, it.next());
}

#[test]
fn test_constant_with_delay() {
let mut exp = ConstantBuilder::default()
let mut it = ConstantBuilder::default()
.with_delay(Duration::from_secs(2))
.build();

assert_eq!(Some(Duration::from_secs(2)), exp.next());
assert_eq!(Some(Duration::from_secs(2)), exp.next());
assert_eq!(Some(Duration::from_secs(2)), exp.next());
assert_eq!(None, exp.next());
assert_eq!(Some(Duration::from_secs(2)), it.next());
assert_eq!(Some(Duration::from_secs(2)), it.next());
assert_eq!(Some(Duration::from_secs(2)), it.next());
assert_eq!(None, it.next());
}

#[test]
fn test_constant_with_times() {
let mut exp = ConstantBuilder::default().with_max_times(1).build();
let mut it = ConstantBuilder::default().with_max_times(1).build();

assert_eq!(Some(Duration::from_secs(1)), exp.next());
assert_eq!(None, exp.next());
assert_eq!(Some(Duration::from_secs(1)), it.next());
assert_eq!(None, it.next());
}

#[test]
Expand All @@ -175,4 +185,13 @@ mod tests {
fastrand::seed(7);
assert!(dur > Duration::from_secs(1));
}

#[test]
fn test_constant_without_max_times() {
let mut it = ConstantBuilder::default().without_max_times().build();

for _ in 0..10_000 {
assert_eq!(Some(Duration::from_secs(1)), it.next());
}
}
}
51 changes: 51 additions & 0 deletions backon/src/backoff/exponential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,33 @@ impl ExponentialBuilder {
self
}

/// Set no maximum delay for the backoff.
///
/// The delay will keep increasing.
///
/// _The delay will saturate at `Duration::MAX` which is an **unrealistic** delay._
pub fn without_max_delay(mut self) -> Self {
self.max_delay = None;
self
}

/// Set the maximum number of attempts for the current backoff.
///
/// The backoff will stop if the maximum number of attempts is reached.
pub fn with_max_times(mut self, max_times: usize) -> Self {
self.max_times = Some(max_times);
self
}

/// Set no maximum number of attempts for the current backoff.
///
/// The backoff will not stop by itself.
///
/// _The backoff could stop reaching `usize::MAX` attempts but this is **unrealistic**._
pub fn without_max_times(mut self) -> Self {
self.max_times = None;
self
}
}

impl BackoffBuilder for ExponentialBuilder {
Expand Down Expand Up @@ -246,6 +266,21 @@ mod tests {
assert_eq!(None, exp.next());
}

#[test]
fn test_exponential_no_max_times_with_default() {
let mut exp = ExponentialBuilder::default()
.with_min_delay(Duration::from_secs(1))
.with_factor(1_f32)
.without_max_times()
.build();

// to fully test we would need to call this `usize::MAX`
// which seems unreasonable for a test as it would take too long...
for _ in 0..10_000 {
assert_eq!(Some(Duration::from_secs(1)), exp.next());
}
}

#[test]
fn test_exponential_max_delay_with_default() {
let mut exp = ExponentialBuilder::default()
Expand All @@ -258,6 +293,22 @@ mod tests {
assert_eq!(None, exp.next());
}

#[test]
fn test_exponential_no_max_delay_with_default() {
let mut exp = ExponentialBuilder::default()
.with_min_delay(Duration::from_secs(1))
.with_factor(10_000_000_000_f32)
.without_max_delay()
.with_max_times(4)
.build();

assert_eq!(Some(Duration::from_secs(1)), exp.next());
assert_eq!(Some(Duration::from_secs(10_000_000_000)), exp.next());
assert_eq!(Some(Duration::MAX), exp.next());
assert_eq!(Some(Duration::MAX), exp.next());
assert_eq!(None, exp.next());
}

#[test]
fn test_exponential_max_delay_without_default_1() {
let mut exp = ExponentialBuilder {
Expand Down
57 changes: 56 additions & 1 deletion backon/src/backoff/fibonacci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,33 @@ impl FibonacciBuilder {
self
}

/// Set no maximum delay for the backoff.
///
/// The delay will keep increasing.
///
/// _The delay will saturate at `Duration::MAX` which is an **unrealistic** delay._
pub fn without_max_delay(mut self) -> Self {
self.max_delay = None;
self
}

/// Set the maximum number of attempts for the current backoff.
///
/// The backoff will stop if the maximum number of attempts is reached.
pub fn with_max_times(mut self, max_times: usize) -> Self {
self.max_times = Some(max_times);
self
}

/// Set no maximum number of attempts for the current backoff.
///
/// The backoff will not stop by itself.
///
/// _The backoff could stop reaching `usize::MAX` attempts but this is **unrealistic**._
pub fn without_max_times(mut self) -> Self {
self.max_times = None;
self
}
}

impl BackoffBuilder for FibonacciBuilder {
Expand Down Expand Up @@ -153,7 +173,7 @@ impl Iterator for FibonacciBackoff {
// If current delay larger than max delay, we should stop increment anymore.
if next < self.max_delay.unwrap_or(Duration::MAX) {
if let Some(prev) = self.previous_delay {
next += prev;
next = next.saturating_add(prev);
self.current_delay = Some(next);
}
self.previous_delay = Some(cur);
Expand Down Expand Up @@ -235,6 +255,27 @@ mod tests {
assert_eq!(None, fib.next());
}

#[test]
fn test_fibonacci_no_max_delay() {
let mut fib = FibonacciBuilder::default()
.with_max_times(4)
.with_min_delay(Duration::from_secs(10_000_000_000_000_000_000))
.without_max_delay()
.build();

assert_eq!(
Some(Duration::from_secs(10_000_000_000_000_000_000)),
fib.next()
);
assert_eq!(
Some(Duration::from_secs(10_000_000_000_000_000_000)),
fib.next()
);
assert_eq!(Some(Duration::MAX), fib.next());
assert_eq!(Some(Duration::MAX), fib.next());
assert_eq!(None, fib.next());
}

#[test]
fn test_fibonacci_max_times() {
let mut fib = FibonacciBuilder::default().with_max_times(6).build();
Expand All @@ -247,4 +288,18 @@ mod tests {
assert_eq!(Some(Duration::from_secs(8)), fib.next());
assert_eq!(None, fib.next());
}

#[test]
fn test_fibonacci_no_max_times() {
let mut fib = FibonacciBuilder::default()
.with_min_delay(Duration::from_secs(0))
.without_max_times()
.build();

// to fully test we would need to call this `usize::MAX`
// which seems unreasonable for a test as it would take too long...
for _ in 0..10_000 {
assert_eq!(Some(Duration::from_secs(0)), fib.next());
}
}
}

0 comments on commit 0e14575

Please sign in to comment.