-
Notifications
You must be signed in to change notification settings - Fork 1
/
retry.go
123 lines (104 loc) · 2.59 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
package retry
import (
"context"
"time"
)
// Default Delay func options
const (
DefaultAttempts uint8 = 10
DefaultDelay = time.Millisecond * 100
DefaultDelayFactor = 1
)
type config struct {
maxAttempts uint8
delay time.Duration
delayFactor int
stopRetryIf StopRetryIfFunc
onRetry OnRetryFunc
}
// Option is a function type for
// manipulating Retry configs
type Option func(*config)
// MaxAttempts is an option to set maximum attempts number
// It could be a integer number between 0-255
// Default is 10
func MaxAttempts(attempts uint8) Option {
return func(c *config) {
c.maxAttempts = attempts
}
}
// Delay is an option to set minimum delay between each retries
// Default is 100ms
func Delay(delay time.Duration) Option {
return func(c *config) {
c.delay = delay
}
}
// DelayFactor is an option to set increase of delay
// duration on each retries
// Actual delay calculated by: (delay * delayFactor * attemptNumber)
// Default is 1
func DelayFactor(factor int) Option {
return func(c *config) {
c.delayFactor = factor
}
}
// StopRetryIfFunc is a function type to set conditions
// To stop continuing the retry mechanism
type StopRetryIfFunc func(ctx context.Context, err error) bool
// StopRetryIf is an option to set StopRetryIfFunc
func StopRetryIf(fn StopRetryIfFunc) Option {
return func(c *config) {
c.stopRetryIf = fn
}
}
// OnRetryFunc is a function type to set some
// functionality to retry function
// It stops retry mechanism when error was not nil
type OnRetryFunc func(ctx context.Context, attempt int) error
// OnRetry is an option to set OnRetryFunc
func OnRetry(fn OnRetryFunc) Option {
return func(c *config) {
c.onRetry = fn
}
}
// Retry tries to handle the operation func
// It tries until maxAttempts exceeded
// At the end of retries returns error from operation func
func Retry(ctx context.Context, operation func() error, opts ...Option) error {
cfg := &config{
maxAttempts: DefaultAttempts,
delay: DefaultDelay,
delayFactor: DefaultDelayFactor,
}
for _, opt := range opts {
opt(cfg)
}
var err error
for attempt := 1; attempt <= int(cfg.maxAttempts); attempt++ {
err = operation()
if err == nil {
return nil
}
if cfg.onRetry != nil {
if err := cfg.onRetry(ctx, attempt); err != nil {
return err
}
}
if attempt == int(cfg.maxAttempts) {
return err
}
if cfg.stopRetryIf != nil {
if cfg.stopRetryIf(ctx, err) {
return err
}
}
select {
case <-ctx.Done():
return err
case <-time.After(cfg.delay * time.Duration(attempt*cfg.delayFactor)):
continue
}
}
return err
}