diff --git a/pkg/time/delay.go b/pkg/time/delay.go index 0c13eb3b602..956e5750ee9 100644 --- a/pkg/time/delay.go +++ b/pkg/time/delay.go @@ -18,6 +18,7 @@ package time import ( "context" + "math/bits" "math/rand" "time" ) @@ -39,8 +40,17 @@ func LinearDelay(ctx context.Context, attempt uint, increment, maxDelay time.Dur // ExponentialDelayWithJitter is an exponential backoff strategy with jitter for retries. It calculates delay based on the attempt number, // adds jitter, and sleeps for that duration, capped at maxDelay. func ExponentialDelayWithJitter(ctx context.Context, attempt uint, baseDelay, maxDelay time.Duration) error { - delay := baseDelay * time.Duration(1< 0 { + delay = maxDelay + if attempt < uint(bits.LeadingZeros64(uint64(baseDelay))) { + delay = min(baseDelay< 0 { jitter := time.Duration(rand.Int63n(int64(delay))) diff --git a/pkg/time/delay_test.go b/pkg/time/delay_test.go index 288ce105859..19f7c1e15df 100644 --- a/pkg/time/delay_test.go +++ b/pkg/time/delay_test.go @@ -149,6 +149,30 @@ func TestExponentialDelayWithJitter(t *testing.T) { expectedMin: 650 * time.Millisecond, expectedMax: 3000 * time.Millisecond, }, + { + name: "overflow attempt stays capped at maxDelay", + attempt: 39, + baseDelay: 30 * time.Millisecond, + maxDelay: 1 * time.Second, + expectedMin: 380 * time.Millisecond, + expectedMax: 1500 * time.Millisecond, + }, + { + name: "shift overflow attempt stays capped at maxDelay", + attempt: 62, + baseDelay: 30 * time.Millisecond, + maxDelay: 1 * time.Second, + expectedMin: 380 * time.Millisecond, + expectedMax: 1500 * time.Millisecond, + }, + { + name: "huge attempt stays capped at maxDelay", + attempt: 100, + baseDelay: 30 * time.Millisecond, + maxDelay: 1 * time.Second, + expectedMin: 380 * time.Millisecond, + expectedMax: 1500 * time.Millisecond, + }, { name: "zero baseDelay with jitter", attempt: 5,