Skip to content

Commit 2b17a41

Browse files
authored
Dealing with a canceled Context (#8)
* TestCancel (fails). * ctx.Done() has priority, so we test it alone first. * If ctx is already canceled, then return immediately without calling f. * Move TestCancel into retry_test.go.
1 parent e6dbee7 commit 2b17a41

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

retry.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ func (e *retryableError) Error() string {
4949
// Do wraps a function with a backoff to retry. The provided context is the same
5050
// context passed to the RetryFunc.
5151
func Do(ctx context.Context, b Backoff, f RetryFunc) error {
52+
// If ctx is already canceled, then return immediately without calling f
53+
select {
54+
case <-ctx.Done():
55+
return ctx.Err()
56+
default:
57+
}
58+
5259
for {
5360
err := f(ctx)
5461
if err == nil {
@@ -66,6 +73,13 @@ func Do(ctx context.Context, b Backoff, f RetryFunc) error {
6673
return rerr.Unwrap()
6774
}
6875

76+
// ctx.Done() has priority, so we test it alone first
77+
select {
78+
case <-ctx.Done():
79+
return ctx.Err()
80+
default:
81+
}
82+
6983
select {
7084
case <-ctx.Done():
7185
return ctx.Err()

retry_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package retry_test
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io"
78
"net/http"
@@ -173,3 +174,34 @@ func ExampleDo_customRetry() {
173174
// handle error
174175
}
175176
}
177+
178+
func TestCancel(t *testing.T) {
179+
for i := 0; i < 100000; i++ {
180+
ctx, cancel := context.WithCancel(context.Background())
181+
182+
calls := 0
183+
rf := func(ctx context.Context) error {
184+
calls++
185+
// Never succeed.
186+
// Always return a RetryableError
187+
return retry.RetryableError(errors.New("nope"))
188+
}
189+
190+
const delay time.Duration = time.Millisecond
191+
b := retry.NewConstant(delay)
192+
193+
const maxRetries = 5
194+
b = retry.WithMaxRetries(maxRetries, b)
195+
196+
const jitter time.Duration = 5 * time.Millisecond
197+
b = retry.WithJitter(jitter, b)
198+
199+
// Here we cancel the Context *before* the call to Do
200+
cancel()
201+
retry.Do(ctx, b, rf)
202+
203+
if calls > 1 {
204+
t.Errorf("rf was called %d times instead of 0 or 1", calls)
205+
}
206+
}
207+
}

0 commit comments

Comments
 (0)