-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathretry.go
99 lines (82 loc) · 2.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
package goretry
import (
"math"
"time"
)
const MaxRetryCount RetryCount = math.MaxUint16
// Instance able to Retry a procedure and RetryReturn a function.
type Retryer[T any] interface {
// Method return last retry's error or nil in case of eventual success.
Retry(RetryFunc) error
// Method return last retry's error and T's default value or nil and T in
// case of eventual success.
RetryReturn(RetryReturningFunc[T]) (T, error)
}
type (
RetryReturningFunc[T any] func() (T, error)
RetryFunc func() error
RetryCount uint16
)
func New[T any](configurers ...RetryConfigurer) Retryer[T] {
rc := NewDefaultRetryConfig()
for _, configurer := range configurers {
configurer(rc)
}
return &retryer[T]{
initialDelay: rc.InitialDelay,
maxRetries: rc.MaxRetries,
delayCalculator: rc.DelayCalculator,
}
}
func WithInitialDelay(delay time.Duration) RetryConfigurer {
return func(rc *RetryConfig) {
rc.InitialDelay = delay
}
}
func WithMaxRetries(maxRetries RetryCount) RetryConfigurer {
return func(rc *RetryConfig) {
rc.MaxRetries = maxRetries
}
}
func WithIncreasingDelay(addition time.Duration) RetryConfigurer {
return WithDelayCalculator(NewIncreasingDelayCalculator(addition))
}
func WithJittingDelay(around time.Duration) RetryConfigurer {
return WithDelayCalculator(NewJittingDelayCalculator(around))
}
func WithDelayCalculator(calc DelayCalculator) RetryConfigurer {
return func(rc *RetryConfig) {
rc.DelayCalculator = calc
}
}
type retryer[T any] struct {
initialDelay time.Duration
maxRetries RetryCount
delayCalculator DelayCalculator
}
func (c *retryer[T]) Retry(fu RetryFunc) error {
var empty T
_, err := c.RetryReturn(func() (T, error) {
return empty, fu()
})
return err
}
func (c *retryer[T]) RetryReturn(fu RetryReturningFunc[T]) (T, error) {
var res T
var err error
currentDelay := c.initialDelay
currentRetries := c.maxRetries
for {
if currentRetries == 0 {
return res, err
}
res, err = fu()
// TODO: Replace raw check with custom predicate
if err == nil {
return res, nil
}
currentRetries--
time.Sleep(currentDelay)
currentDelay = c.delayCalculator(currentDelay)
}
}