diff --git a/core/retry/backoff/backoff.go b/core/retry/backoff/backoff.go index b3612dd4..a021d492 100644 --- a/core/retry/backoff/backoff.go +++ b/core/retry/backoff/backoff.go @@ -5,6 +5,7 @@ import ( "time" ) +// Func defines the method to calculate how long to retry. type Func func(attempt int) time.Duration // LinearWithJitter waits a set period of time, allowing for jitter (fractional adjustment). diff --git a/core/retry/backoff/backoff_test.go b/core/retry/backoff/backoff_test.go index 73a64471..bce2b68d 100644 --- a/core/retry/backoff/backoff_test.go +++ b/core/retry/backoff/backoff_test.go @@ -16,3 +16,15 @@ func TestExponential(t *testing.T) { fn := Exponential(time.Second) assert.EqualValues(t, time.Second, fn(1)) } + +func TestLinearWithJitter(t *testing.T) { + const rounds = 1000000 + var total time.Duration + fn := LinearWithJitter(time.Second, 0.5) + for i := 0; i < rounds; i++ { + total += fn(1) + } + + // 0.1% tolerance + assert.True(t, total/time.Duration(rounds)-time.Second < time.Millisecond) +} diff --git a/core/retry/options_test.go b/core/retry/options_test.go index 6ac0bf4b..608c20ae 100644 --- a/core/retry/options_test.go +++ b/core/retry/options_test.go @@ -50,6 +50,8 @@ func TestRetryWithPerRetryTimeout(t *testing.T) { } func Test_waitRetryBackoff(t *testing.T) { + logx.Disable() + opt := &options{perCallTimeout: time.Second, backoffFunc: func(attempt int) time.Duration { return time.Second }} diff --git a/core/retry/retryinterceptor.go b/core/retry/retryinterceptor.go index 435a1979..0ff9615b 100644 --- a/core/retry/retryinterceptor.go +++ b/core/retry/retryinterceptor.go @@ -52,10 +52,11 @@ func waitRetryBackoff(logger logx.Logger, attempt int, ctx context.Context, retr } if waitTime > 0 { timer := time.NewTimer(waitTime) + defer timer.Stop() + logger.Infof("grpc retry attempt: %d, backoff for %v", attempt, waitTime) select { case <-ctx.Done(): - timer.Stop() return status.FromContextError(ctx.Err()).Err() case <-timer.C: // double check