Merge pull request #20 from tukeJonny/retrier-runctx
Add RunCtx to retrier
Evan Huus authored 5 years ago
GitHub committed 5 years ago
0 | 0 |
language: go
|
1 | 1 |
|
2 | 2 |
go:
|
3 | |
- 1.2
|
4 | |
- 1.6
|
5 | |
- 1.10
|
|
3 |
- 1.7
|
|
4 |
- "1.10"
|
1 | 1 |
package retrier
|
2 | 2 |
|
3 | 3 |
import (
|
|
4 |
"context"
|
4 | 5 |
"math/rand"
|
5 | 6 |
"sync"
|
6 | 7 |
"time"
|
|
32 | 33 |
}
|
33 | 34 |
}
|
34 | 35 |
|
35 | |
// Run executes the given work function, then classifies its return value based on the classifier used
|
|
36 |
// Run executes the given work function by executing RunCtx without context.Context.
|
|
37 |
func (r *Retrier) Run(work func() error) error {
|
|
38 |
return r.RunCtx(context.Background(), func(ctx context.Context) error {
|
|
39 |
// never use ctx
|
|
40 |
return work()
|
|
41 |
})
|
|
42 |
}
|
|
43 |
|
|
44 |
// RunCtx executes the given work function, then classifies its return value based on the classifier used
|
36 | 45 |
// to construct the Retrier. If the result is Succeed or Fail, the return value of the work function is
|
37 | 46 |
// returned to the caller. If the result is Retry, then Run sleeps according to the its backoff policy
|
38 | 47 |
// before retrying. If the total number of retries is exceeded then the return value of the work function
|
39 | 48 |
// is returned to the caller regardless.
|
40 | |
func (r *Retrier) Run(work func() error) error {
|
|
49 |
func (r *Retrier) RunCtx(ctx context.Context, work func(ctx context.Context) error) error {
|
41 | 50 |
retries := 0
|
42 | 51 |
for {
|
43 | |
ret := work()
|
|
52 |
ret := work(ctx)
|
44 | 53 |
|
45 | 54 |
switch r.class.Classify(ret) {
|
46 | 55 |
case Succeed, Fail:
|
|
49 | 58 |
if retries >= len(r.backoff) {
|
50 | 59 |
return ret
|
51 | 60 |
}
|
52 | |
time.Sleep(r.calcSleep(retries))
|
|
61 |
|
|
62 |
timeout := time.After(r.calcSleep(retries))
|
|
63 |
if err := r.sleep(ctx, timeout); err != nil {
|
|
64 |
return err
|
|
65 |
}
|
|
66 |
|
53 | 67 |
retries++
|
54 | 68 |
}
|
|
69 |
}
|
|
70 |
}
|
|
71 |
|
|
72 |
func (r *Retrier) sleep(ctx context.Context, t <-chan time.Time) error {
|
|
73 |
select {
|
|
74 |
case <-t:
|
|
75 |
return nil
|
|
76 |
case <-ctx.Done():
|
|
77 |
return ctx.Err()
|
55 | 78 |
}
|
56 | 79 |
}
|
57 | 80 |
|
0 | 0 |
package retrier
|
1 | 1 |
|
2 | 2 |
import (
|
|
3 |
"context"
|
3 | 4 |
"errors"
|
4 | 5 |
"testing"
|
5 | 6 |
"time"
|
|
15 | 16 |
return nil
|
16 | 17 |
}
|
17 | 18 |
return returns[i-1]
|
|
19 |
}
|
|
20 |
}
|
|
21 |
|
|
22 |
func genWorkWithCtx() func(ctx context.Context) error {
|
|
23 |
i = 0
|
|
24 |
return func(ctx context.Context) error {
|
|
25 |
select {
|
|
26 |
case <-ctx.Done():
|
|
27 |
return errFoo
|
|
28 |
default:
|
|
29 |
i++
|
|
30 |
}
|
|
31 |
return nil
|
18 | 32 |
}
|
19 | 33 |
}
|
20 | 34 |
|
|
42 | 56 |
t.Error(err)
|
43 | 57 |
}
|
44 | 58 |
if i != 1 {
|
|
59 |
t.Error("run wrong number of times")
|
|
60 |
}
|
|
61 |
}
|
|
62 |
|
|
63 |
func TestRetrierCtx(t *testing.T) {
|
|
64 |
ctx, cancel := context.WithCancel(context.Background())
|
|
65 |
|
|
66 |
r := New([]time.Duration{0, 10 * time.Millisecond}, WhitelistClassifier{})
|
|
67 |
|
|
68 |
err := r.RunCtx(ctx, genWorkWithCtx())
|
|
69 |
if err != nil {
|
|
70 |
t.Error(err)
|
|
71 |
}
|
|
72 |
if i != 1 {
|
|
73 |
t.Error("run wrong number of times")
|
|
74 |
}
|
|
75 |
|
|
76 |
cancel()
|
|
77 |
|
|
78 |
err = r.RunCtx(ctx, genWorkWithCtx())
|
|
79 |
if err != errFoo {
|
|
80 |
t.Error("context must be cancelled")
|
|
81 |
}
|
|
82 |
if i != 0 {
|
45 | 83 |
t.Error("run wrong number of times")
|
46 | 84 |
}
|
47 | 85 |
}
|