From 0e8291b22d6358be9fc76a72591a4b344e715af6 Mon Sep 17 00:00:00 2001 From: dentinyhao Date: Tue, 31 Mar 2026 16:09:28 -0700 Subject: [PATCH] fix(backoff): fibonacci delay should cap on max delay --- backon/src/backoff/fibonacci.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/backon/src/backoff/fibonacci.rs b/backon/src/backoff/fibonacci.rs index d711808..01478b3 100644 --- a/backon/src/backoff/fibonacci.rs +++ b/backon/src/backoff/fibonacci.rs @@ -199,11 +199,18 @@ impl Iterator for FibonacciBackoff { if next < self.max_delay.unwrap_or(Duration::MAX) { if let Some(prev) = self.previous_delay { next = next.saturating_add(prev); - self.current_delay = Some(next); } self.previous_delay = Some(cur); } + // Clamp to max_delay so a Fibonacci step can never overshoot. + if let Some(max_delay) = self.max_delay { + if next > max_delay { + next = max_delay; + } + } + self.current_delay = Some(next); + // If jitter is enabled, add random jitter based on min delay. if self.jitter { next += self.min_delay.mul_f32(self.rng.f32()); @@ -285,6 +292,23 @@ mod tests { assert_eq!(None, fib.next()); } + #[test] + fn test_fibonacci_max_delay_clamp() { + let mut fib = FibonacciBuilder::default() + .with_max_times(7) + .with_max_delay(Duration::from_secs(4)) + .build(); + + assert_eq!(Some(Duration::from_secs(1)), fib.next()); + assert_eq!(Some(Duration::from_secs(1)), fib.next()); + assert_eq!(Some(Duration::from_secs(2)), fib.next()); + assert_eq!(Some(Duration::from_secs(3)), fib.next()); + assert_eq!(Some(Duration::from_secs(4)), fib.next()); + assert_eq!(Some(Duration::from_secs(4)), fib.next()); + assert_eq!(Some(Duration::from_secs(4)), fib.next()); + assert_eq!(None, fib.next()); + } + #[test] fn test_fibonacci_no_max_delay() { let mut fib = FibonacciBuilder::default()