Codebase list golang-github-avast-retry-go / 0222b1c
Update upstream source from tag 'upstream/2.4.3' Update to upstream version '2.4.3' with Debian dir 267ee032d725bc5e3242d47f7397116cf362757c Dmitry Smirnov 4 years ago
9 changed file(s) with 291 addition(s) and 67 deletion(s). Raw diff Collapse all Expand all
1818
1919 # cover
2020 coverage.txt
21
22 .pc
44 - 1.7
55 - 1.8
66 - 1.9
7 - "1.10"
8 - 1.11
9 - 1.12
710
811 install:
912 - make setup
22 TEST_OPTIONS?=
33 DEP?=$$(which dep)
44 VERSION?=$$(cat VERSION)
5 LINTER?=$$(which golangci-lint)
6 LINTER_VERSION=1.15.0
57
68 ifeq ($(OS),Windows_NT)
79 DEP_VERS=dep-windows-amd64
10 LINTER_FILE=golangci-lint-$(LINTER_VERSION)-windows-amd64.zip
11 LINTER_UNPACK= >| app.zip; unzip -j app.zip -d $$GOPATH/bin; rm app.zip
12 else ifeq ($(OS), Darwin)
13 LINTER_FILE=golangci-lint-$(LINTER_VERSION)-darwin-amd64.tar.gz
14 LINTER_UNPACK= | tar xzf - -C $$GOPATH/bin --wildcards --strip 1 "**/golangci-lint"
815 else
916 DEP_VERS=dep-linux-amd64
17 LINTER_FILE=golangci-lint-$(LINTER_VERSION)-linux-amd64.tar.gz
18 LINTER_UNPACK= | tar xzf - -C $$GOPATH/bin --wildcards --strip 1 "**/golangci-lint"
1019 endif
1120
12 setup: ## Install all the build and lint dependencies
13 # fix of gopkg.in issue (https://github.com/niemeyer/gopkg/issues/50)
14 git config --global http.https://gopkg.in.followRedirects true
15 go get -u gopkg.in/alecthomas/gometalinter.v1
21 setup:
1622 go get -u github.com/pierrre/gotestcover
1723 go get -u golang.org/x/tools/cmd/cover
1824 go get -u github.com/robertkrimen/godocdown/godocdown
19 gometalinter.v1 --install
25 @if [ "$(LINTER)" = "" ]; then\
26 curl -L https://github.com/golangci/golangci-lint/releases/download/v$(LINTER_VERSION)/$(LINTER_FILE) $(LINTER_UNPACK) ;\
27 chmod +x $$GOPATH/bin/golangci-lint;\
28 fi
2029 @if [ "$(DEP)" = "" ]; then\
2130 curl -L https://github.com/golang/dep/releases/download/v0.3.1/$(DEP_VERS) >| $$GOPATH/bin/dep;\
2231 chmod +x $$GOPATH/bin/dep;\
2635 generate: ## Generate README.md
2736 godocdown >| README.md
2837
29 test: generate ## Run all the tests
38 test: generate test_and_cover_report lint
39
40 test_and_cover_report:
3041 gotestcover $(TEST_OPTIONS) -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m
3142
3243 cover: test ## Run all the tests and opens the coverage report
3647 find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
3748
3849 lint: ## Run all the linters
39 gometalinter.v1 --vendor --disable-all \
40 --enable=deadcode \
41 --enable=ineffassign \
42 --enable=gosimple \
43 --enable=staticcheck \
44 --enable=gofmt \
45 --enable=goimports \
46 --enable=dupl \
47 --enable=misspell \
48 --enable=errcheck \
49 --enable=vet \
50 --deadline=10m \
51 ./...
50 golangci-lint run
5251
53 ci: test lint ## Run all the tests and code checks
52 ci: test_and_cover_report ## Run all the tests but no linters - use https://golangci.com integration instead
5453
5554 build:
5655 go build
5756
5857 release: ## Release new version
59 git tag | grep -q $(VERSION) && echo This version was released! Increase VERSION! || git tag $(VERSION) && git push origin $(VERSION)
58 git tag | grep -q $(VERSION) && echo This version was released! Increase VERSION! || git tag $(VERSION) && git push origin $(VERSION) && git tag v$(VERSION) && git push origin v$(VERSION)
6059
6160 # Absolutely awesome: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
6261 help:
6363
6464 ### BREAKING CHANGES
6565
66 1.0.2 -> 2.0.0
67
68 * argument of `retry.Delay` is final delay (no multiplication by `retry.Units`
69 anymore)
70
71 * function `retry.Units` are removed
72
73 * [more about this breaking change](https://github.com/avast/retry-go/issues/7)
74
6675 0.3.0 -> 1.0.0
6776
6877 * `retry.Retry` function are changed to `retry.Do` function
7281
7382 ## Usage
7483
84 #### func BackOffDelay
85
86 ```go
87 func BackOffDelay(n uint, config *Config) time.Duration
88 ```
89 BackOffDelay is a DelayType which increases delay between consecutive retries
90
7591 #### func Do
7692
7793 ```go
7894 func Do(retryableFunc RetryableFunc, opts ...Option) error
7995 ```
96
97 #### func FixedDelay
98
99 ```go
100 func FixedDelay(_ uint, config *Config) time.Duration
101 ```
102 FixedDelay is a DelayType which keeps delay the same through all iterations
103
104 #### func IsRecoverable
105
106 ```go
107 func IsRecoverable(err error) bool
108 ```
109 IsRecoverable checks if error is an instance of `unrecoverableError`
110
111 #### func Unrecoverable
112
113 ```go
114 func Unrecoverable(err error) unrecoverableError
115 ```
116 Unrecoverable wraps an error in `unrecoverableError` struct
117
118 #### type Config
119
120 ```go
121 type Config struct {
122 }
123 ```
124
125
126 #### type DelayTypeFunc
127
128 ```go
129 type DelayTypeFunc func(n uint, config *Config) time.Duration
130 ```
131
80132
81133 #### type Error
82134
115167 #### type Option
116168
117169 ```go
118 type Option func(*config)
170 type Option func(*Config)
119171 ```
120172
121173 Option represents an option for retry.
132184 ```go
133185 func Delay(delay time.Duration) Option
134186 ```
135 Delay set delay between retry default are 1e5 units
187 Delay set delay between retry default is 100ms
188
189 #### func DelayType
190
191 ```go
192 func DelayType(delayType DelayTypeFunc) Option
193 ```
194 DelayType set type of the delay between retries default is BackOff
195
196 #### func LastErrorOnly
197
198 ```go
199 func LastErrorOnly(lastErrorOnly bool) Option
200 ```
201 return the direct last error that came from the retried function default is
202 false (return wrapped errors with everything)
136203
137204 #### func OnRetry
138205
147214 func() error {
148215 return errors.New("some error")
149216 },
150 retry.OnRetry(func(n unit, err error) {
217 retry.OnRetry(func(n uint, err error) {
151218 log.Printf("#%d: %s\n", n, err)
152219 }),
153220 )
174241 })
175242 )
176243
177 #### func Units
178
179 ```go
180 func Units(units time.Duration) Option
181 ```
182 Units set unit of delay (probably only for tests purpose) default are
183 microsecond
244 The default RetryIf stops execution if the error is wrapped using
245 `retry.Unrecoverable`, so above example may also be shortened to:
246
247 retry.Do(
248 func() error {
249 return retry.Unrecoverable(errors.New("special error"))
250 }
251 )
184252
185253 #### type RetryIfFunc
186254
0 1.0.2
0 2.4.3
0 package retry_test
1
2 import (
3 "io/ioutil"
4 "net/http"
5 "testing"
6 "time"
7
8 "github.com/avast/retry-go"
9 "github.com/stretchr/testify/assert"
10 )
11
12 func TestCustomRetryFunction(t *testing.T) {
13 url := "http://example.com"
14 var body []byte
15
16 err := retry.Do(
17 func() error {
18 resp, err := http.Get(url)
19
20 if err == nil {
21 defer func() {
22 if err := resp.Body.Close(); err != nil {
23 panic(err)
24 }
25 }()
26 body, err = ioutil.ReadAll(resp.Body)
27 }
28
29 return err
30 },
31 retry.DelayType(func(n uint, config *retry.Config) time.Duration {
32 return 0
33 }),
34 )
35
36 assert.NoError(t, err)
37 assert.NotEmpty(t, body)
38 }
1010 // n = count of attempts
1111 type OnRetryFunc func(n uint, err error)
1212
13 type config struct {
14 attempts uint
15 delay time.Duration
16 units time.Duration
17 onRetry OnRetryFunc
18 retryIf RetryIfFunc
13 type DelayTypeFunc func(n uint, config *Config) time.Duration
14
15 type Config struct {
16 attempts uint
17 delay time.Duration
18 onRetry OnRetryFunc
19 retryIf RetryIfFunc
20 delayType DelayTypeFunc
21 lastErrorOnly bool
1922 }
2023
2124 // Option represents an option for retry.
22 type Option func(*config)
25 type Option func(*Config)
26
27 // return the direct last error that came from the retried function
28 // default is false (return wrapped errors with everything)
29 func LastErrorOnly(lastErrorOnly bool) Option {
30 return func(c *Config) {
31 c.lastErrorOnly = lastErrorOnly
32 }
33 }
2334
2435 // Attempts set count of retry
2536 // default is 10
2637 func Attempts(attempts uint) Option {
27 return func(c *config) {
38 return func(c *Config) {
2839 c.attempts = attempts
2940 }
3041 }
3142
3243 // Delay set delay between retry
33 // default are 1e5 units
44 // default is 100ms
3445 func Delay(delay time.Duration) Option {
35 return func(c *config) {
46 return func(c *Config) {
3647 c.delay = delay
3748 }
3849 }
3950
40 // Units set unit of delay (probably only for tests purpose)
41 // default are microsecond
42 func Units(units time.Duration) Option {
43 return func(c *config) {
44 c.units = units
51 // DelayType set type of the delay between retries
52 // default is BackOff
53 func DelayType(delayType DelayTypeFunc) Option {
54 return func(c *Config) {
55 c.delayType = delayType
4556 }
57 }
58
59 // BackOffDelay is a DelayType which increases delay between consecutive retries
60 func BackOffDelay(n uint, config *Config) time.Duration {
61 return config.delay * (1 << n)
62 }
63
64 // FixedDelay is a DelayType which keeps delay the same through all iterations
65 func FixedDelay(_ uint, config *Config) time.Duration {
66 return config.delay
4667 }
4768
4869 // OnRetry function callback are called each retry
5374 // func() error {
5475 // return errors.New("some error")
5576 // },
56 // retry.OnRetry(func(n unit, err error) {
77 // retry.OnRetry(func(n uint, err error) {
5778 // log.Printf("#%d: %s\n", n, err)
5879 // }),
5980 // )
6081 func OnRetry(onRetry OnRetryFunc) Option {
61 return func(c *config) {
82 return func(c *Config) {
6283 c.onRetry = onRetry
6384 }
6485 }
79100 // return true
80101 // })
81102 // )
103 //
104 // By default RetryIf stops execution if the error is wrapped using `retry.Unrecoverable`,
105 // so above example may also be shortened to:
106 //
107 // retry.Do(
108 // func() error {
109 // return retry.Unrecoverable(errors.New("special error"))
110 // }
111 // )
82112 func RetryIf(retryIf RetryIfFunc) Option {
83 return func(c *config) {
113 return func(c *Config) {
84114 c.retryIf = retryIf
85115 }
86116 }
4444
4545 BREAKING CHANGES
4646
47 1.0.2 -> 2.0.0
48
49 * argument of `retry.Delay` is final delay (no multiplication by `retry.Units` anymore)
50
51 * function `retry.Units` are removed
52
53 * [more about this breaking change](https://github.com/avast/retry-go/issues/7)
54
55
4756 0.3.0 -> 1.0.0
4857
4958 * `retry.Retry` function are changed to `retry.Do` function
6776 var n uint
6877
6978 //default
70 config := &config{
71 attempts: 10,
72 delay: 1e5,
73 units: time.Microsecond,
74 onRetry: func(n uint, err error) {},
75 retryIf: func(err error) bool { return true },
79 config := &Config{
80 attempts: 10,
81 delay: 100 * time.Millisecond,
82 onRetry: func(n uint, err error) {},
83 retryIf: IsRecoverable,
84 delayType: BackOffDelay,
85 lastErrorOnly: false,
7686 }
7787
7888 //apply opts
8090 opt(config)
8191 }
8292
83 errorLog := make(Error, config.attempts)
93 var errorLog Error
94 if !config.lastErrorOnly {
95 errorLog = make(Error, config.attempts)
96 } else {
97 errorLog = make(Error, 1)
98 }
8499
100 lastErrIndex := n
85101 for n < config.attempts {
86102 err := retryableFunc()
87103
88104 if err != nil {
89 config.onRetry(n, err)
90 errorLog[n] = err
105 errorLog[lastErrIndex] = unpackUnrecoverable(err)
91106
92107 if !config.retryIf(err) {
93108 break
94109 }
110
111 config.onRetry(n, err)
95112
96113 // if this is last attempt - don't wait
97114 if n == config.attempts-1 {
98115 break
99116 }
100117
101 delayTime := config.delay * (1 << (n - 1))
102 time.Sleep((time.Duration)(delayTime) * config.units)
118 delayTime := config.delayType(n, config)
119 time.Sleep(delayTime)
103120 } else {
104121 return nil
105122 }
106123
107124 n++
125 if !config.lastErrorOnly {
126 lastErrIndex = n
127 }
108128 }
109129
130 if config.lastErrorOnly {
131 return errorLog[lastErrIndex]
132 }
110133 return errorLog
111134 }
112135
143166 func (e Error) WrappedErrors() []error {
144167 return e
145168 }
169
170 type unrecoverableError struct {
171 error
172 }
173
174 // Unrecoverable wraps an error in `unrecoverableError` struct
175 func Unrecoverable(err error) error {
176 return unrecoverableError{err}
177 }
178
179 // IsRecoverable checks if error is an instance of `unrecoverableError`
180 func IsRecoverable(err error) bool {
181 _, isUnrecoverable := err.(unrecoverableError)
182 return !isUnrecoverable
183 }
184
185 func unpackUnrecoverable(err error) error {
186 if unrecoverable, isUnrecoverable := err.(unrecoverableError); isUnrecoverable {
187 return unrecoverable.error
188 }
189
190 return err
191 }
33 "errors"
44 "testing"
55 "time"
6
7 "fmt"
68
79 "github.com/stretchr/testify/assert"
810 )
1214 err := Do(
1315 func() error { return errors.New("test") },
1416 OnRetry(func(n uint, err error) { retrySum += n }),
15 Units(time.Nanosecond),
17 Delay(time.Nanosecond),
1618 )
1719 assert.Error(t, err)
1820
5658 RetryIf(func(err error) bool {
5759 return err.Error() != "special"
5860 }),
59 Units(time.Nanosecond),
61 Delay(time.Nanosecond),
6062 )
6163 assert.Error(t, err)
6264
6567 #2: test
6668 #3: special`
6769 assert.Equal(t, expectedErrorFormat, err.Error(), "retry error format")
68 assert.Equal(t, uint(3), retryCount, "right count of retry")
70 assert.Equal(t, uint(2), retryCount, "right count of retry")
6971
7072 }
7173
7779 )
7880 dur := time.Since(start)
7981 assert.Error(t, err)
80 assert.True(t, dur > 10*time.Millisecond, "3 times default retry is longer then 10ms")
82 assert.True(t, dur > 300*time.Millisecond, "3 times default retry is longer then 300ms")
8183 }
84
85 func TestFixedSleep(t *testing.T) {
86 start := time.Now()
87 err := Do(
88 func() error { return errors.New("test") },
89 Attempts(3),
90 DelayType(FixedDelay),
91 )
92 dur := time.Since(start)
93 assert.Error(t, err)
94 assert.True(t, dur < 500*time.Millisecond, "3 times default retry is shorter then 500ms")
95 }
96
97 func TestLastErrorOnly(t *testing.T) {
98 var retrySum uint
99 err := Do(
100 func() error { return fmt.Errorf("%d", retrySum) },
101 OnRetry(func(n uint, err error) { retrySum += 1 }),
102 Delay(time.Nanosecond),
103 LastErrorOnly(true),
104 )
105 assert.Error(t, err)
106 assert.Equal(t, "9", err.Error())
107 }
108
109 func TestUnrecoverableError(t *testing.T) {
110 attempts := 0
111 expectedErr := errors.New("error")
112 err := Do(
113 func() error {
114 attempts++
115 return Unrecoverable(expectedErr)
116 },
117 Attempts(2),
118 LastErrorOnly(true),
119 )
120 assert.Equal(t, expectedErr, err)
121 assert.Equal(t, 1, attempts, "unrecoverable error broke the loop")
122 }