Skip to content

Commit 5e47352

Browse files
authored
Merge pull request #3 from avast/retry_if
retryIf
2 parents 0556298 + e31ba39 commit 5e47352

File tree

7 files changed

+242
-121
lines changed

7 files changed

+242
-121
lines changed

README.md

+72-39
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ http get with retry:
2222
url := "http://example.com"
2323
var body []byte
2424

25-
err := retry.Retry(
25+
err := retry.Do(
2626
func() error {
2727
resp, err := http.Get(url)
2828
if err != nil {
@@ -61,29 +61,23 @@ slightly similar as this package, don't have 'simple' `Retry` method
6161
* [matryer/try](https://github.com/matryer/try) - very popular package,
6262
nonintuitive interface (for me)
6363

64-
## Usage
6564

66-
#### func Retry
65+
### BREAKING CHANGES
6766

68-
```go
69-
func Retry(retryableFunction Retryable) error
70-
```
71-
Retry - simple retry
67+
0.3.0 -> 1.0.0
7268

73-
#### func RetryCustom
69+
* `retry.Retry` function are changed to `retry.Do` function
7470

75-
```go
76-
func RetryCustom(retryableFunction Retryable, onRetryFunction OnRetry, opts RetryOpts) error
77-
```
78-
RetryCustom - the most customizable retry is possible set OnRetry function
79-
callback which are called each retry
71+
* `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are
72+
now implement via functions produces Options (aka `retry.OnRetryFunction`)
73+
74+
## Usage
8075

81-
#### func RetryWithOpts
76+
#### func Do
8277

8378
```go
84-
func RetryWithOpts(retryableFunction Retryable, opts RetryOpts) error
79+
func Do(retryableFunc RetryableFunc, opts ...Option) error
8580
```
86-
RetryWithOpts - customizable retry via RetryOpts
8781

8882
#### type Error
8983

@@ -111,57 +105,96 @@ implementation of the `errwrap.Wrapper` interface in package
111105
[errwrap](https://github.com/hashicorp/errwrap) so that `retry.Error` can be
112106
used with that library.
113107

114-
#### type OnRetry
108+
#### type OnRetryFunc
115109

116110
```go
117-
type OnRetry func(n uint, err error)
111+
type OnRetryFunc func(n uint, err error)
118112
```
119113

120-
Function signature of OnRetry function n = count of tries
114+
Function signature of OnRetry function n = count of attempts
121115

122-
#### type RetryOpts
116+
#### type Option
123117

124118
```go
125-
type RetryOpts struct {
126-
}
119+
type Option func(*config)
127120
```
128121

129-
Struct for configure retry tries - count of tries delay - waiting time units -
130-
waiting time unit (for tests purpose)
122+
Option represents an option for retry.
131123

132-
#### func NewRetryOpts
124+
#### func Attempts
133125

134126
```go
135-
func NewRetryOpts() RetryOpts
127+
func Attempts(attempts uint) Option
136128
```
137-
Create new RetryOpts struct with default values default tries are 10 default
138-
delay are 1e5 default units are microsecond
129+
Attempts set count of retry default is 10
139130

140-
#### func (RetryOpts) Delay
131+
#### func Delay
141132

142133
```go
143-
func (opts RetryOpts) Delay(delay time.Duration) RetryOpts
134+
func Delay(delay time.Duration) Option
144135
```
145-
Delay setter
136+
Delay set delay between retry default are 1e5 units
146137

147-
#### func (RetryOpts) Tries
138+
#### func OnRetry
148139

149140
```go
150-
func (opts RetryOpts) Tries(tries uint) RetryOpts
141+
func OnRetry(onRetry OnRetryFunc) Option
151142
```
152-
Tries setter
143+
OnRetry function callback are called each retry
153144

154-
#### func (RetryOpts) Units
145+
log each retry example:
146+
147+
retry.Do(
148+
func() error {
149+
return errors.New("some error")
150+
},
151+
retry.OnRetry(func(n unit, err error) {
152+
log.Printf("#%d: %s\n", n, err)
153+
}),
154+
)
155+
156+
#### func RetryIf
155157

156158
```go
157-
func (opts RetryOpts) Units(timeUnit time.Duration) RetryOpts
159+
func RetryIf(retryIf RetryIfFunc) Option
158160
```
159-
Units setter
161+
RetryIf controls whether a retry should be attempted after an error (assuming
162+
there are any retry attempts remaining)
163+
164+
skip retry if special error example:
165+
166+
retry.Do(
167+
func() error {
168+
return errors.New("special error")
169+
},
170+
retry.RetryIf(func(err error) bool {
171+
if err.Error() == "special error" {
172+
return false
173+
}
174+
return true
175+
})
176+
)
177+
178+
#### func Units
179+
180+
```go
181+
func Units(units time.Duration) Option
182+
```
183+
Units set unit of delay (probably only for tests purpose) default are
184+
microsecond
185+
186+
#### type RetryIfFunc
187+
188+
```go
189+
type RetryIfFunc func(error) bool
190+
```
191+
192+
Function signature of retry if function
160193

161-
#### type Retryable
194+
#### type RetryableFunc
162195

163196
```go
164-
type Retryable func() error
197+
type RetryableFunc func() error
165198
```
166199

167200
Function signature of retryable function

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.3.0
1+
1.0.0

examples/http_get_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func TestGet(t *testing.T) {
1313
url := "http://example.com"
1414
var body []byte
1515

16-
err := retry.Retry(
16+
err := retry.Do(
1717
func() error {
1818
resp, err := http.Get(url)
1919

options.go

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package retry
2+
3+
import (
4+
"time"
5+
)
6+
7+
// Function signature of retry if function
8+
type RetryIfFunc func(error) bool
9+
10+
// Function signature of OnRetry function
11+
// n = count of attempts
12+
type OnRetryFunc func(n uint, err error)
13+
14+
type config struct {
15+
attempts uint
16+
delay time.Duration
17+
units time.Duration
18+
onRetry OnRetryFunc
19+
retryIf RetryIfFunc
20+
}
21+
22+
// Option represents an option for retry.
23+
type Option func(*config)
24+
25+
// Attempts set count of retry
26+
// default is 10
27+
func Attempts(attempts uint) Option {
28+
return func(c *config) {
29+
c.attempts = attempts
30+
}
31+
}
32+
33+
// Delay set delay between retry
34+
// default are 1e5 units
35+
func Delay(delay time.Duration) Option {
36+
return func(c *config) {
37+
c.delay = delay
38+
}
39+
}
40+
41+
// Units set unit of delay (probably only for tests purpose)
42+
// default are microsecond
43+
func Units(units time.Duration) Option {
44+
return func(c *config) {
45+
c.units = units
46+
}
47+
}
48+
49+
// OnRetry function callback are called each retry
50+
//
51+
// log each retry example:
52+
//
53+
// retry.Do(
54+
// func() error {
55+
// return errors.New("some error")
56+
// },
57+
// retry.OnRetry(func(n unit, err error) {
58+
// log.Printf("#%d: %s\n", n, err)
59+
// }),
60+
// )
61+
func OnRetry(onRetry OnRetryFunc) Option {
62+
return func(c *config) {
63+
c.onRetry = onRetry
64+
}
65+
}
66+
67+
// RetryIf controls whether a retry should be attempted after an error
68+
// (assuming there are any retry attempts remaining)
69+
//
70+
// skip retry if special error example:
71+
//
72+
// retry.Do(
73+
// func() error {
74+
// return errors.New("special error")
75+
// },
76+
// retry.RetryIf(func(err error) bool {
77+
// if err.Error() == "special error" {
78+
// return false
79+
// }
80+
// return true
81+
// })
82+
// )
83+
func RetryIf(retryIf RetryIfFunc) Option {
84+
return func(c *config) {
85+
c.retryIf = retryIf
86+
}
87+
}

retry.go

+49-28
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ http get with retry:
1010
url := "http://example.com"
1111
var body []byte
1212
13-
err := retry.Retry(
13+
err := retry.Do(
1414
func() error {
1515
resp, err := http.Get(url)
1616
if err != nil {
@@ -43,6 +43,15 @@ SEE ALSO
4343
4444
* [matryer/try](https://github.com/matryer/try) - very popular package, nonintuitive interface (for me)
4545
46+
BREAKING CHANGES
47+
48+
0.3.0 -> 1.0.0
49+
50+
* `retry.Retry` function are changed to `retry.Do` function
51+
52+
* `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are now implement via functions produces Options (aka `retry.OnRetry`)
53+
54+
4655
*/
4756
package retry
4857

@@ -53,39 +62,39 @@ import (
5362
)
5463

5564
// Function signature of retryable function
56-
type Retryable func() error
65+
type RetryableFunc func() error
5766

58-
// Function signature of OnRetry function
59-
// n = count of tries
60-
type OnRetry func(n uint, err error)
61-
62-
// Retry - simple retry
63-
func Retry(retryableFunction Retryable) error {
64-
return RetryWithOpts(retryableFunction, NewRetryOpts())
65-
}
67+
func Do(retryableFunc RetryableFunc, opts ...Option) error {
68+
var n uint
6669

67-
// RetryWithOpts - customizable retry via RetryOpts
68-
func RetryWithOpts(retryableFunction Retryable, opts RetryOpts) error {
69-
return RetryCustom(retryableFunction, func(n uint, err error) {}, opts)
70-
}
70+
//default
71+
config := &config{
72+
attempts: 10,
73+
delay: 1e5,
74+
onRetry: func(n uint, err error) {},
75+
retryIf: func(err error) bool { return true },
76+
}
7177

72-
// RetryCustom - the most customizable retry
73-
// is possible set OnRetry function callback
74-
// which are called each retry
75-
func RetryCustom(retryableFunction Retryable, onRetryFunction OnRetry, opts RetryOpts) error {
76-
var n uint
78+
//apply opts
79+
for _, opt := range opts {
80+
opt(config)
81+
}
7782

78-
errorLog := make(Error, opts.tries)
83+
errorLog := make(Error, config.attempts)
7984

80-
for n < opts.tries {
81-
err := retryableFunction()
85+
for n < config.attempts {
86+
err := retryableFunc()
8287

8388
if err != nil {
84-
onRetryFunction(n, err)
89+
config.onRetry(n, err)
8590
errorLog[n] = err
8691

87-
delayTime := opts.delay * (1 << (n - 1))
88-
time.Sleep((time.Duration)(delayTime) * opts.units)
92+
if !config.retryIf(err) {
93+
break
94+
}
95+
96+
delayTime := config.delay * (1 << (n - 1))
97+
time.Sleep((time.Duration)(delayTime) * config.units)
8998
} else {
9099
return nil
91100
}
@@ -102,12 +111,24 @@ type Error []error
102111
// Error method return string representation of Error
103112
// It is an implementation of error interface
104113
func (e Error) Error() string {
105-
logWithNumber := make([]string, len(e))
114+
logWithNumber := make([]string, lenWithoutNil(e))
106115
for i, l := range e {
107-
logWithNumber[i] = fmt.Sprintf("#%d: %s", i+1, l.Error())
116+
if l != nil {
117+
logWithNumber[i] = fmt.Sprintf("#%d: %s", i+1, l.Error())
118+
}
119+
}
120+
121+
return fmt.Sprintf("All attempts fail:\n%s", strings.Join(logWithNumber, "\n"))
122+
}
123+
124+
func lenWithoutNil(e Error) (count int) {
125+
for _, v := range e {
126+
if v != nil {
127+
count++
128+
}
108129
}
109130

110-
return fmt.Sprintf("All retries fail:\n%s", strings.Join(logWithNumber, "\n"))
131+
return
111132
}
112133

113134
// WrappedErrors returns the list of errors that this Error is wrapping.

0 commit comments

Comments
 (0)