-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathretry.go
93 lines (78 loc) · 2.09 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
package retry
import (
"context"
"time"
)
// RetryFunc defines the function signature for functions that can be retried
type RetryFunc func(ctx context.Context) error
// // Run uses the default options to call and then retry a function, using an exponential backoff with the
// // given base delay, and limited to a specific number of retries
// func Run(
// ctx context.Context,
// retryLimit Limit,
// baseDelay time.Duration,
// fn RetryFunc,
// ) error {
// return RunWithOptions(
// ctx,
// retryLimit,
// ExponentialBackoff(baseDelay),
// fn,
// )
// }
// RunWithContext runs a retryable function, retrying it whenever an eligible error is encountered,
// until the maximum number of retries has been met, at which point the latest non-nil error
// is returned.
func Run(
ctx context.Context,
retryLimit Limit,
delay Delay,
fn RetryFunc,
) error {
// Ensure there is some context, if nil is passed.
if ctx == nil {
ctx = context.Background()
}
// Track the latest error
var lastError error
// Loop until our limiter runs out. Note that if the retryLimit is negative, it runs forever
for iteration := 0; retryLimit < 0 || Limit(iteration) <= retryLimit; iteration++ {
// If this is not the first iteration, we need to sleep before retrying
if iteration > 0 {
sleepDuration := delay(iteration)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(sleepDuration):
break
}
}
// Run the function and store the error
err := fn(ctx)
shouldRetry := isErrRetryable(err)
if err != nil {
lastError = err
}
// If we should retry, retry
if shouldRetry {
continue
}
// If we should not retry, return the error (it might be nil in the case of success)
return err
}
// If we get here, we exceeded the maximum number of retries
if lastError == nil {
lastError = ErrTooManyRetries
} else if err, ok := lastError.(*retryableErr); ok {
// Pull out the wrapped raw error instance
lastError = err.err
}
return lastError
}
func isErrRetryable(err error) bool {
if err == nil {
return false
}
_, ok := err.(*retryableErr)
return ok
}