diff --git a/retry/CHANGELOG.md b/retry/CHANGELOG.md index 2e3cf7fd..2c60c6af 100644 --- a/retry/CHANGELOG.md +++ b/retry/CHANGELOG.md @@ -2,6 +2,8 @@ ## To be Released +* fix(retry): do not call the method if the context is already done + ## v1.4.1 * chore(deps): bump multiple dependencies diff --git a/retry/retry.go b/retry/retry.go index 77b02f11..96e49b21 100644 --- a/retry/retry.go +++ b/retry/retry.go @@ -166,6 +166,14 @@ func (r Retryer) Do(ctx context.Context, method Retryable) error { var err error for attempt := 0; attempt < r.maxAttempts; attempt++ { + if ctx.Err() != nil { + return RetryError{ + Scope: ContextScope, + Err: ctx.Err(), + LastErr: err, + } + } + err = method(ctx) if err == nil { return nil diff --git a/retry/retry_test.go b/retry/retry_test.go index 3fda4e2d..6065f947 100644 --- a/retry/retry_test.go +++ b/retry/retry_test.go @@ -130,6 +130,28 @@ func TestRetrier(t *testing.T) { }) }) + t.Run("When the context is already canceled, it should not call the method", func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + retrier := New(WithWaitDuration(1 * time.Millisecond)) + tries := 0 + ctx, cancel := context.WithCancel(t.Context()) + cancel() + + err := retrier.Do(ctx, func(ctx context.Context) error { + tries++ + return nil + }) + synctest.Wait() + + var retryError RetryError + require.ErrorAs(t, err, &retryError) + assert.Equal(t, ContextScope, retryError.Scope) + require.ErrorIs(t, retryError.Err, context.Canceled) + require.NoError(t, retryError.LastErr) + assert.Equal(t, 0, tries) + }) + }) + t.Run("With timeout should ignore sleep", func(t *testing.T) { synctest.Test(t, func(t *testing.T) { retrier := New(WithWaitDuration(1 * time.Second))