-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathretry.go
128 lines (99 loc) · 2.27 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*
Retry is a utility package to to facilitate operations that need to be retried
when they fail.
*/
package retry
import (
"fmt"
"github.com/mediafly/math"
"github.com/mediafly/math/constants"
"golang.org/x/net/context"
"time"
)
// Retry represents an operation that needs to be retried until is finishes
// successfully.
type Retry interface {
Do() error
}
type retry struct {
Options
}
// Returns a new Retry operation based on Options.
func New(options Options) Retry {
return &retry{options}
}
func (r *retry) String() string {
return fmt.Sprintf("[%T Tag=%v]", r, r.Tag)
}
// Perform the operation until it completes successfully or retry thresholds
// are crossed.
func (r *retry) Do() error {
debugLog := r.DebugLog
if debugLog == nil {
debugLog = defaultDebugLogger
}
errorLog := r.ErrorLog
if errorLog == nil {
errorLog = defaultErrorLogger
}
maxAttempts := constants.MaxUint32
if r.MaxAttempts > 0 {
maxAttempts = r.MaxAttempts
}
maxDelay := time.Second * 30
if r.MaxDelay >= 0 {
maxDelay = r.MaxDelay
}
deadline := constants.MaxTime
if r.Deadline.After(time.Now().UTC()) {
deadline = r.Deadline
}
ctx := r.Context
if ctx == nil {
ctx = context.Background()
}
var attempt uint32 = 0
var result Result = &continueResult{nil}
for {
if result, ok := result.(*stopResult); ok {
if debugLog != nil {
debugLog(r, "completed")
}
return result.Err
}
{
result := result.(*continueResult)
if attempt >= maxAttempts {
if errorLog != nil {
errorLog(r, "failed, max attempts:", result.Err)
}
return result.Err
}
if attempt == 0 {
if debugLog != nil {
debugLog(r, "started")
}
} else {
if errorLog != nil {
errorLog(r, "retrying attempt", attempt+1, "of", maxAttempts, ":", result.Err)
}
}
delay := math.MinDuration(maxDelay, time.Second*time.Duration(attempt*attempt))
select {
case <-ctx.Done():
if errorLog != nil {
errorLog(r, "cancelled:", result.Err)
}
return &CancelledError{r, result.Err}
case <-time.After(deadline.Sub(time.Now().UTC())):
if errorLog != nil {
errorLog(r, "passed deadline:", result.Err)
}
return &DeadlineError{r, result.Err}
case <-time.After(delay):
}
}
result = r.Options.Do()
attempt++
}
}