1 package backoff
2
3 import (
4 "errors"
5 "time"
6 )
7
8
9
10 type OperationWithData[T any] func() (T, error)
11
12
13
14 type Operation func() error
15
16 func (o Operation) withEmptyData() OperationWithData[struct{}] {
17 return func() (struct{}, error) {
18 return struct{}{}, o()
19 }
20 }
21
22
23
24
25
26
27 type Notify func(error, time.Duration)
28
29
30
31
32
33
34
35
36
37 func Retry(o Operation, b BackOff) error {
38 return RetryNotify(o, b, nil)
39 }
40
41
42 func RetryWithData[T any](o OperationWithData[T], b BackOff) (T, error) {
43 return RetryNotifyWithData(o, b, nil)
44 }
45
46
47
48 func RetryNotify(operation Operation, b BackOff, notify Notify) error {
49 return RetryNotifyWithTimer(operation, b, notify, nil)
50 }
51
52
53 func RetryNotifyWithData[T any](operation OperationWithData[T], b BackOff, notify Notify) (T, error) {
54 return doRetryNotify(operation, b, notify, nil)
55 }
56
57
58
59
60 func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer) error {
61 _, err := doRetryNotify(operation.withEmptyData(), b, notify, t)
62 return err
63 }
64
65
66 func RetryNotifyWithTimerAndData[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) {
67 return doRetryNotify(operation, b, notify, t)
68 }
69
70 func doRetryNotify[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) {
71 var (
72 err error
73 next time.Duration
74 res T
75 )
76 if t == nil {
77 t = &defaultTimer{}
78 }
79
80 defer func() {
81 t.Stop()
82 }()
83
84 ctx := getContext(b)
85
86 b.Reset()
87 for {
88 res, err = operation()
89 if err == nil {
90 return res, nil
91 }
92
93 var permanent *PermanentError
94 if errors.As(err, &permanent) {
95 return res, permanent.Err
96 }
97
98 if next = b.NextBackOff(); next == Stop {
99 if cerr := ctx.Err(); cerr != nil {
100 return res, cerr
101 }
102
103 return res, err
104 }
105
106 if notify != nil {
107 notify(err, next)
108 }
109
110 t.Start(next)
111
112 select {
113 case <-ctx.Done():
114 return res, ctx.Err()
115 case <-t.C():
116 }
117 }
118 }
119
120
121 type PermanentError struct {
122 Err error
123 }
124
125 func (e *PermanentError) Error() string {
126 return e.Err.Error()
127 }
128
129 func (e *PermanentError) Unwrap() error {
130 return e.Err
131 }
132
133 func (e *PermanentError) Is(target error) bool {
134 _, ok := target.(*PermanentError)
135 return ok
136 }
137
138
139 func Permanent(err error) error {
140 if err == nil {
141 return nil
142 }
143 return &PermanentError{
144 Err: err,
145 }
146 }
147
View as plain text