-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathretry.go
104 lines (91 loc) · 3.17 KB
/
retry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package async
import (
"context"
"time"
)
const (
defaultRetryTimes int = 5
defaultRetryInterval int = 0
)
type RetryOptions struct {
// Times is the number of attempts to make before giving up, the default is 5.
Times int
// Interval is the time to wait between retries in milliseconds, the default is 0.
Interval int
// IntervalFunc is the function to calculate the time to wait between retries in milliseconds, it
// accepts an int value to indicate the retry count.
IntervalFunc func(int) int
// ErrorFilter is a function that is invoked on an error result. Retry will continue the retry
// attempts if it returns true, and it will abort the workflow and return the current attempt's
// result and error if it returns false.
ErrorFilter func(error) bool
}
// Retry attempts to get a successful response from the function with no more than the specific
// retry times before returning an error. If the task is successful, it will return the result of
// the successful task. If all attempts fail, it will return the result and the error of the final
// attempt.
//
// async.Retry(func() error {
// // Do something
// return err
// }) // Run the function 5 times without interval time or it succeed
//
// async.Retry(func() error {
// // Do something
// return err
// }, RetryOptions{
// Times: 3,
// Interval: 100,
// }) // Run the function 3 times with 100ms interval or it succeed
func Retry(fn AsyncFn, opts ...RetryOptions) ([]any, error) {
return retry(context.Background(), fn, opts...)
}
// RetryWithContext runs the function with the specified context, and attempts to get a successful
// response from the function with no more than the specific retry times before returning an error.
// If the task is successful, it will return the result of the successful task. If all attempts
// fail, it will return the result and the error of the final attempt.
func RetryWithContext(ctx context.Context, fn AsyncFn, opts ...RetryOptions) ([]any, error) {
return retry(ctx, fn, opts...)
}
// retry runs the function and attempts to get a successful response from the function with no more
// than the specific retry times before returning an error.
func retry(parent context.Context, fn AsyncFn, opts ...RetryOptions) (out []any, err error) {
validateAsyncFuncs(fn)
ctx := getContext(parent)
opt := getRetryOption(opts...)
for i := 1; i <= opt.Times; i++ {
out, err = invokeAsyncFn(fn, ctx, nil)
if err == nil {
return
} else if opt.ErrorFilter != nil && !opt.ErrorFilter(err) {
return
}
if i != opt.Times {
interval := opt.Interval
if opt.IntervalFunc != nil {
interval = opt.IntervalFunc(i)
}
if interval != 0 {
time.Sleep(time.Duration(interval) * time.Millisecond)
}
}
}
return
}
// getRetryOption gets the retry option by the customize option or the default values.
func getRetryOption(opts ...RetryOptions) RetryOptions {
opt := RetryOptions{}
if len(opts) > 0 {
opt.Interval = opts[0].Interval
opt.Times = opts[0].Times
opt.IntervalFunc = opts[0].IntervalFunc
opt.ErrorFilter = opts[0].ErrorFilter
}
if opt.Interval <= 0 {
opt.Interval = defaultRetryInterval
}
if opt.Times <= 0 {
opt.Times = defaultRetryTimes
}
return opt
}