Codebase list golang-github-avast-retry-go / 339799f0-8f07-4c22-a072-a73fcb89ab43/upstream/4.3.0+git20221115.1.affbf8f+ds
Import upstream version 4.3.0+git20221115.1.affbf8f+ds Debian Janitor 1 year, 5 months ago
12 changed file(s) with 314 addition(s) and 171 deletion(s). Raw diff Collapse all Expand all
+0
-21
.gitignore less more
0 # Binaries for programs and plugins
1 *.exe
2 *.dll
3 *.so
4 *.dylib
5
6 # Test binary, build with `go test -c`
7 *.test
8
9 # Output of the go coverage tool, specifically when used with LiteIDE
10 *.out
11
12 # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
13 .glide/
14
15 # dep
16 vendor/
17 Gopkg.lock
18
19 # cover
20 coverage.txt
11
22 [![Release](https://img.shields.io/github/release/avast/retry-go.svg?style=flat-square)](https://github.com/avast/retry-go/releases/latest)
33 [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
4 [![Travis](https://img.shields.io/travis/avast/retry-go.svg?style=flat-square)](https://travis-ci.org/avast/retry-go)
5 [![AppVeyor](https://ci.appveyor.com/api/projects/status/fieg9gon3qlq0a9a?svg=true)](https://ci.appveyor.com/project/JaSei/retry-go)
4 ![GitHub Actions](https://github.com/avast/retry-go/actions/workflows/workflow.yaml/badge.svg)
65 [![Go Report Card](https://goreportcard.com/badge/github.com/avast/retry-go?style=flat-square)](https://goreportcard.com/report/github.com/avast/retry-go)
76 [![GoDoc](https://godoc.org/github.com/avast/retry-go?status.svg&style=flat-square)](http://godoc.org/github.com/avast/retry-go)
87 [![codecov.io](https://codecov.io/github/avast/retry-go/coverage.svg?branch=master)](https://codecov.io/github/avast/retry-go?branch=master)
2423
2524 ### Before pull request
2625
26 > maybe you need `make setup` in order to setup environment
27
2728 please try:
2829 * run tests (`make test`)
2930 * run linter (`make lint`)
+0
-15
.travis.yml less more
0 language: go
1
2 go:
3 - 1.13
4 - 1.14
5 - 1.15
6
7 install:
8 - make setup
9
10 script:
11 - make ci
12
13 after_success:
14 - bash <(curl -s https://codecov.io/bash)
22 TEST_OPTIONS?=
33 VERSION?=$$(cat VERSION)
44 LINTER?=$$(which golangci-lint)
5 LINTER_VERSION=1.15.0
5 LINTER_VERSION=1.50.0
66
77 ifeq ($(OS),Windows_NT)
88 LINTER_FILE=golangci-lint-$(LINTER_VERSION)-windows-amd64.zip
1616 endif
1717
1818 setup:
19 go get -u github.com/pierrre/gotestcover
20 go get -u golang.org/x/tools/cmd/cover
21 go get -u github.com/robertkrimen/godocdown/godocdown
22 @if [ "$(LINTER)" = "" ]; then\
23 curl -L https://github.com/golangci/golangci-lint/releases/download/v$(LINTER_VERSION)/$(LINTER_FILE) $(LINTER_UNPACK) ;\
24 chmod +x $$GOPATH/bin/golangci-lint;\
25 fi
19 go install github.com/pierrre/gotestcover@latest
20 go install golang.org/x/tools/cmd/cover@latest
21 go install github.com/robertkrimen/godocdown/godocdown@latest
2622 go mod download
2723
2824 generate: ## Generate README.md
4036 find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
4137
4238 lint: ## Run all the linters
39 @if [ "$(LINTER)" = "" ]; then\
40 curl -L https://github.com/golangci/golangci-lint/releases/download/v$(LINTER_VERSION)/$(LINTER_FILE) $(LINTER_UNPACK) ;\
41 chmod +x $$GOPATH/bin/golangci-lint;\
42 fi
43
4344 golangci-lint run
4445
4546 ci: test_and_cover_report ## Run all the tests but no linters - use https://golangci.com integration instead
11
22 [![Release](https://img.shields.io/github/release/avast/retry-go.svg?style=flat-square)](https://github.com/avast/retry-go/releases/latest)
33 [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
4 [![Travis](https://img.shields.io/travis/avast/retry-go.svg?style=flat-square)](https://travis-ci.org/avast/retry-go)
5 [![AppVeyor](https://ci.appveyor.com/api/projects/status/fieg9gon3qlq0a9a?svg=true)](https://ci.appveyor.com/project/JaSei/retry-go)
4 ![GitHub Actions](https://github.com/avast/retry-go/actions/workflows/workflow.yaml/badge.svg)
65 [![Go Report Card](https://goreportcard.com/badge/github.com/avast/retry-go?style=flat-square)](https://goreportcard.com/report/github.com/avast/retry-go)
76 [![GoDoc](https://godoc.org/github.com/avast/retry-go?status.svg&style=flat-square)](http://godoc.org/github.com/avast/retry-go)
87 [![codecov.io](https://codecov.io/github/avast/retry-go/coverage.svg?branch=master)](https://codecov.io/github/avast/retry-go?branch=master)
1312 slightly inspired by
1413 [Try::Tiny::Retry](https://metacpan.org/pod/Try::Tiny::Retry)
1514
16
17 ### SYNOPSIS
15 # SYNOPSIS
1816
1917 http get with retry:
2018
4139
4240 [next examples](https://github.com/avast/retry-go/tree/master/examples)
4341
44
45 ### SEE ALSO
42 # SEE ALSO
4643
4744 * [giantswarm/retry-go](https://github.com/giantswarm/retry-go) - slightly
4845 complicated interface.
6057 * [matryer/try](https://github.com/matryer/try) - very popular package,
6158 nonintuitive interface (for me)
6259
63
64 ### BREAKING CHANGES
60 # BREAKING CHANGES
6561
6662 * 4.0.0
6763
68 * infinity retry is possible by set `Attempts(0)` by PR [#49](https://github.com/avast/retry-go/pull/49)
64 - infinity retry is possible by set `Attempts(0)` by PR [#49](https://github.com/avast/retry-go/pull/49)
6965
7066 * 3.0.0
7167
72 * `DelayTypeFunc` accepts a new parameter `err` - this breaking change affects only your custom Delay Functions. This change allow [make delay functions based on error](examples/delay_based_on_error_test.go).
68 - `DelayTypeFunc` accepts a new parameter `err` - this breaking change affects only your custom Delay Functions. This change allow [make delay functions based on error](examples/delay_based_on_error_test.go).
7369
7470 * 1.0.2 -> 2.0.0
7571
76 * argument of `retry.Delay` is final delay (no multiplication by `retry.Units` anymore)
77 * function `retry.Units` are removed
78 * [more about this breaking change](https://github.com/avast/retry-go/issues/7)
72 - argument of `retry.Delay` is final delay (no multiplication by `retry.Units` anymore)
73 - function `retry.Units` are removed
74 - [more about this breaking change](https://github.com/avast/retry-go/issues/7)
7975
8076 * 0.3.0 -> 1.0.0
8177
82 * `retry.Retry` function are changed to `retry.Do` function
83 * `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are now implement via functions produces Options (aka `retry.OnRetry`)
78 - `retry.Retry` function are changed to `retry.Do` function
79 - `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are now implement via functions produces Options (aka `retry.OnRetry`)
8480
8581 ## Usage
8682
158154
159155 Error type represents list of errors in retry
160156
157 #### func (Error) As
158
159 ```go
160 func (e Error) As(target interface{}) bool
161 ```
162
161163 #### func (Error) Error
162164
163165 ```go
165167 ```
166168 Error method return string representation of Error It is an implementation of
167169 error interface
170
171 #### func (Error) Is
172
173 ```go
174 func (e Error) Is(target error) bool
175 ```
176
177 #### func (Error) Unwrap
178
179 ```go
180 func (e Error) Unwrap() error
181 ```
182 Unwrap the last error for compatible with the `errors.Unwrap()` when you need
183 unwrap all erros, you should use `WrappedErrors()` instead
184
185 err := Do(
186 func() error {
187 return errors.New("original error")
188 },
189 Attempts(1),
190 )
191
192 fmt.Println(errors.Unwrap(err)) # "original error" is printed
193
194 added in version 4.2.0
168195
169196 #### func (Error) WrappedErrors
170197
199226 ```
200227 Attempts set count of retry. Setting to 0 will retry until the retried function
201228 succeeds. default is 10
229
230 #### func AttemptsForError
231
232 ```go
233 func AttemptsForError(attempts uint, err error) Option
234 ```
235 AttemptsForError sets count of retry in case execution results in given `err`
236 Retries for the given `err` are also counted against total retries. The retry
237 will stop if any of given retries is exhausted.
238
239 added in 4.3.0
202240
203241 #### func Context
204242
305343 }
306344 )
307345
346 #### func WithTimer
347
348 ```go
349 func WithTimer(t Timer) Option
350 ```
351 WithTimer provides a way to swap out timer module implementations. This
352 primarily is useful for mocking/testing, where you may not want to explicitly
353 wait for a set duration for retries.
354
355 example of augmenting time.After with a print statement
356
357 type struct MyTimer {}
358
359 func (t *MyTimer) After(d time.Duration) <- chan time.Time {
360 fmt.Print("Timer called!")
361 return time.After(d)
362 }
363
364 retry.Do(
365
366 func() error { ... },
367 retry.WithTimer(&MyTimer{})
368
369 )
370
308371 #### type RetryIfFunc
309372
310373 ```go
321384
322385 Function signature of retryable function
323386
387 #### type Timer
388
389 ```go
390 type Timer interface {
391 After(time.Duration) <-chan time.Time
392 }
393 ```
394
395 Timer represents the timer used to track time for a retry.
396
324397 ## Contributing
325398
326399 Contributions are very much welcome.
332405 Try `make help` for more information.
333406
334407 ### Before pull request
408
409 > maybe you need `make setup` in order to setup environment
335410
336411 please try:
337412 * run tests (`make test`)
0 4.0.3
0 4.3.0
+0
-19
appveyor.yml less more
0 version: "{build}"
1
2 clone_folder: c:\Users\appveyor\go\src\github.com\avast\retry-go
3
4 #os: Windows Server 2012 R2
5 platform: x64
6
7 install:
8 - copy c:\MinGW\bin\mingw32-make.exe c:\MinGW\bin\make.exe
9 - set GOPATH=C:\Users\appveyor\go
10 - set PATH=%PATH%;c:\MinGW\bin
11 - set PATH=%PATH%;%GOPATH%\bin;c:\go\bin
12 - set GOBIN=%GOPATH%\bin
13 - go version
14 - go env
15 - make setup
16
17 build_script:
18 - make ci
11
22 go 1.13
33
4 require (
5 github.com/pierrre/gotestcover v0.0.0-20160517101806-924dca7d15f0 // indirect
6 github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481 // indirect
7 github.com/stretchr/testify v1.7.0
8 golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 // indirect
9 golang.org/x/tools v0.1.7 // indirect
10 )
4 require github.com/stretchr/testify v1.8.1
0 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
10 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 github.com/pierrre/gotestcover v0.0.0-20160517101806-924dca7d15f0 h1:i5VIxp6QB8oWZ8IkK8zrDgeT6ORGIUeiN+61iETwJbI=
3 github.com/pierrre/gotestcover v0.0.0-20160517101806-924dca7d15f0/go.mod h1:4xpMLz7RBWyB+ElzHu8Llua96TRCB3YwX+l5EP1wmHk=
1 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
43 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
54 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6 github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
75 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
8 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
9 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
10 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
11 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
12 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
13 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
14 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
15 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
16 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
17 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
18 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
19 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
20 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
21 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
22 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
23 golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
24 golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
25 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
26 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
27 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
28 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
29 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
30 golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
31 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
32 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
33 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
6 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
7 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
8 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
9 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
10 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
11 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
3412 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
3513 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
36 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
3714 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
15 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
16 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2222 }
2323
2424 type Config struct {
25 attempts uint
26 delay time.Duration
27 maxDelay time.Duration
28 maxJitter time.Duration
29 onRetry OnRetryFunc
30 retryIf RetryIfFunc
31 delayType DelayTypeFunc
32 lastErrorOnly bool
33 context context.Context
34 timer Timer
25 attempts uint
26 attemptsForError map[error]uint
27 delay time.Duration
28 maxDelay time.Duration
29 maxJitter time.Duration
30 onRetry OnRetryFunc
31 retryIf RetryIfFunc
32 delayType DelayTypeFunc
33 lastErrorOnly bool
34 context context.Context
35 timer Timer
3536
3637 maxBackOffN uint
3738 }
5455 func Attempts(attempts uint) Option {
5556 return func(c *Config) {
5657 c.attempts = attempts
58 }
59 }
60
61 // AttemptsForError sets count of retry in case execution results in given `err`
62 // Retries for the given `err` are also counted against total retries.
63 // The retry will stop if any of given retries is exhausted.
64 //
65 // added in 4.3.0
66 func AttemptsForError(attempts uint, err error) Option {
67 return func(c *Config) {
68 c.attemptsForError[err] = attempts
5769 }
5870 }
5971
220232 // example of augmenting time.After with a print statement
221233 //
222234 // type struct MyTimer {}
223 // func (t *MyTimer) After(d time.Duration) <- chan time.Time {
224 // fmt.Print("Timer called!")
225 // return time.After(d)
226 // }
227 //
235 //
236 // func (t *MyTimer) After(d time.Duration) <- chan time.Time {
237 // fmt.Print("Timer called!")
238 // return time.After(d)
239 // }
228240 //
229241 // retry.Do(
230 // func() error { ... },
231 // retry.WithTimer(&MyTimer{})
242 //
243 // func() error { ... },
244 // retry.WithTimer(&MyTimer{})
245 //
232246 // )
233 //
234247 func WithTimer(t Timer) Option {
235248 return func(c *Config) {
236249 c.timer = t
22
33 slightly inspired by [Try::Tiny::Retry](https://metacpan.org/pod/Try::Tiny::Retry)
44
5 SYNOPSIS
5 # SYNOPSIS
66
77 http get with retry:
88
2929
3030 [next examples](https://github.com/avast/retry-go/tree/master/examples)
3131
32
33 SEE ALSO
32 # SEE ALSO
3433
3534 * [giantswarm/retry-go](https://github.com/giantswarm/retry-go) - slightly complicated interface.
3635
4241
4342 * [matryer/try](https://github.com/matryer/try) - very popular package, nonintuitive interface (for me)
4443
45
46 BREAKING CHANGES
44 # BREAKING CHANGES
4745
4846 * 4.0.0
49 * infinity retry is possible by set `Attempts(0)` by PR [#49](https://github.com/avast/retry-go/pull/49)
47 - infinity retry is possible by set `Attempts(0)` by PR [#49](https://github.com/avast/retry-go/pull/49)
48
5049 * 3.0.0
51 * `DelayTypeFunc` accepts a new parameter `err` - this breaking change affects only your custom Delay Functions. This change allow [make delay functions based on error](examples/delay_based_on_error_test.go).
50 - `DelayTypeFunc` accepts a new parameter `err` - this breaking change affects only your custom Delay Functions. This change allow [make delay functions based on error](examples/delay_based_on_error_test.go).
51
5252 * 1.0.2 -> 2.0.0
53 * argument of `retry.Delay` is final delay (no multiplication by `retry.Units` anymore)
54 * function `retry.Units` are removed
55 * [more about this breaking change](https://github.com/avast/retry-go/issues/7)
53 - argument of `retry.Delay` is final delay (no multiplication by `retry.Units` anymore)
54 - function `retry.Units` are removed
55 - [more about this breaking change](https://github.com/avast/retry-go/issues/7)
56
5657 * 0.3.0 -> 1.0.0
57 * `retry.Retry` function are changed to `retry.Do` function
58 * `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are now implement via functions produces Options (aka `retry.OnRetry`)
59
60
58 - `retry.Retry` function are changed to `retry.Do` function
59 - `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are now implement via functions produces Options (aka `retry.OnRetry`)
6160 */
6261 package retry
6362
6463 import (
6564 "context"
65 "errors"
6666 "fmt"
6767 "strings"
6868 "time"
100100
101101 config.onRetry(n, err)
102102 select {
103 case <-time.After(delay(config, n, err)):
103 case <-config.timer.After(delay(config, n, err)):
104104 case <-config.context.Done():
105105 return nil
106106 }
116116 errorLog = make(Error, 1)
117117 }
118118
119 attemptsForError := make(map[error]uint, len(config.attemptsForError))
120 for err, attempts := range config.attemptsForError {
121 attemptsForError[err] = attempts
122 }
123
119124 lastErrIndex := n
120 for n < config.attempts {
125 shouldRetry := true
126 for shouldRetry {
121127 err := retryableFunc()
122128
123129 if err != nil {
128134 }
129135
130136 config.onRetry(n, err)
137
138 for errToCheck, attempts := range attemptsForError {
139 if errors.Is(err, errToCheck) {
140 attempts--
141 attemptsForError[errToCheck] = attempts
142 shouldRetry = shouldRetry && attempts > 0
143 }
144 }
131145
132146 // if this is last attempt - don't wait
133147 if n == config.attempts-1 {
140154 if config.lastErrorOnly {
141155 return config.context.Err()
142156 }
157 n++
143158 errorLog[n] = config.context.Err()
144159 return errorLog
145160 }
149164 }
150165
151166 n++
167 shouldRetry = shouldRetry && n < config.attempts
168
152169 if !config.lastErrorOnly {
153170 lastErrIndex = n
154171 }
162179
163180 func newDefaultRetryConfig() *Config {
164181 return &Config{
165 attempts: uint(10),
166 delay: 100 * time.Millisecond,
167 maxJitter: 100 * time.Millisecond,
168 onRetry: func(n uint, err error) {},
169 retryIf: IsRecoverable,
170 delayType: CombineDelay(BackOffDelay, RandomDelay),
171 lastErrorOnly: false,
172 context: context.Background(),
173 timer: &timerImpl{},
182 attempts: uint(10),
183 attemptsForError: make(map[error]uint),
184 delay: 100 * time.Millisecond,
185 maxJitter: 100 * time.Millisecond,
186 onRetry: func(n uint, err error) {},
187 retryIf: IsRecoverable,
188 delayType: CombineDelay(BackOffDelay, RandomDelay),
189 lastErrorOnly: false,
190 context: context.Background(),
191 timer: &timerImpl{},
174192 }
175193 }
176194
190208 return fmt.Sprintf("All attempts fail:\n%s", strings.Join(logWithNumber, "\n"))
191209 }
192210
211 func (e Error) Is(target error) bool {
212 for _, v := range e {
213 if errors.Is(v, target) {
214 return true
215 }
216 }
217 return false
218 }
219
220 func (e Error) As(target interface{}) bool {
221 for _, v := range e {
222 if errors.As(v, target) {
223 return true
224 }
225 }
226 return false
227 }
228
229 /*
230 Unwrap the last error for compatible with the `errors.Unwrap()`
231 when you need unwrap all erros, you should use `WrappedErrors()` instead
232
233 err := Do(
234 func() error {
235 return errors.New("original error")
236 },
237 Attempts(1),
238 )
239
240 fmt.Println(errors.Unwrap(err)) # "original error" is printed
241
242 added in version 4.2.0
243 */
244 func (e Error) Unwrap() error {
245 return e[len(e)-1]
246 }
247
193248 func lenWithoutNil(e Error) (count int) {
194249 for _, v := range e {
195250 if v != nil {
33 "context"
44 "errors"
55 "fmt"
6 "os"
67 "testing"
78 "time"
89
108109 assert.Equal(t, count, 1)
109110 }
110111
112 func TestAttemptsForError(t *testing.T) {
113 count := uint(0)
114 testErr := os.ErrInvalid
115 attemptsForTestError := uint(3)
116 err := Do(
117 func() error {
118 count++
119 return testErr
120 },
121 AttemptsForError(attemptsForTestError, testErr),
122 Attempts(5),
123 )
124 assert.Error(t, err)
125 assert.Equal(t, attemptsForTestError, count)
126 }
127
111128 func TestDefaultSleep(t *testing.T) {
112129 start := time.Now()
113130 err := Do(
116133 )
117134 dur := time.Since(start)
118135 assert.Error(t, err)
119 assert.True(t, dur > 300*time.Millisecond, "3 times default retry is longer then 300ms")
136 assert.Greater(t, dur, 300*time.Millisecond, "3 times default retry is longer then 300ms")
120137 }
121138
122139 func TestFixedSleep(t *testing.T) {
128145 )
129146 dur := time.Since(start)
130147 assert.Error(t, err)
131 assert.True(t, dur < 500*time.Millisecond, "3 times default retry is shorter then 500ms")
148 assert.Less(t, dur, 500*time.Millisecond, "3 times default retry is shorter then 500ms")
132149 }
133150
134151 func TestLastErrorOnly(t *testing.T) {
159176 }
160177
161178 func TestCombineFixedDelays(t *testing.T) {
179 if os.Getenv("OS") == "macos-latest" {
180 t.Skip("Skipping testing in MacOS GitHub actions - too slow, duration is wrong")
181 }
182
162183 start := time.Now()
163184 err := Do(
164185 func() error { return errors.New("test") },
167188 )
168189 dur := time.Since(start)
169190 assert.Error(t, err)
170 assert.True(t, dur > 400*time.Millisecond, "3 times combined, fixed retry is longer then 400ms")
171 assert.True(t, dur < 500*time.Millisecond, "3 times combined, fixed retry is shorter then 500ms")
191 assert.Greater(t, dur, 400*time.Millisecond, "3 times combined, fixed retry is greater then 400ms")
192 assert.Less(t, dur, 500*time.Millisecond, "3 times combined, fixed retry is less then 500ms")
172193 }
173194
174195 func TestRandomDelay(t *testing.T) {
196 if os.Getenv("OS") == "macos-latest" {
197 t.Skip("Skipping testing in MacOS GitHub actions - too slow, duration is wrong")
198 }
199
175200 start := time.Now()
176201 err := Do(
177202 func() error { return errors.New("test") },
181206 )
182207 dur := time.Since(start)
183208 assert.Error(t, err)
184 assert.True(t, dur > 2*time.Millisecond, "3 times random retry is longer then 2ms")
185 assert.True(t, dur < 100*time.Millisecond, "3 times random retry is shorter then 100ms")
209 assert.Greater(t, dur, 2*time.Millisecond, "3 times random retry is longer then 2ms")
210 assert.Less(t, dur, 150*time.Millisecond, "3 times random retry is shorter then 150ms")
186211 }
187212
188213 func TestMaxDelay(t *testing.T) {
214 if os.Getenv("OS") == "macos-latest" {
215 t.Skip("Skipping testing in MacOS GitHub actions - too slow, duration is wrong")
216 }
217
189218 start := time.Now()
190219 err := Do(
191220 func() error { return errors.New("test") },
195224 )
196225 dur := time.Since(start)
197226 assert.Error(t, err)
198 assert.True(t, dur > 120*time.Millisecond, "5 times with maximum delay retry is longer than 120ms")
199 assert.True(t, dur < 205*time.Millisecond, "5 times with maximum delay retry is shorter than 205ms")
227 assert.Greater(t, dur, 120*time.Millisecond, "5 times with maximum delay retry is less than 120ms")
228 assert.Less(t, dur, 250*time.Millisecond, "5 times with maximum delay retry is longer than 250ms")
200229 }
201230
202231 func TestBackOffDelay(t *testing.T) {
334363
335364 expectedErrorFormat := `All attempts fail:
336365 #1: test
337 #2: context canceled`
366 #2: test
367 #3: context canceled`
338368 assert.Equal(t, expectedErrorFormat, err.Error(), "retry error format")
339369 assert.Equal(t, 2, retrySum, "called at most once")
340370 })
421451 assert.Error(t, err)
422452
423453 }
454
455 func TestErrorIs(t *testing.T) {
456 var e Error
457 expectErr := errors.New("error")
458 closedErr := os.ErrClosed
459 e = append(e, expectErr)
460 e = append(e, closedErr)
461
462 assert.True(t, errors.Is(e, expectErr))
463 assert.True(t, errors.Is(e, closedErr))
464 assert.False(t, errors.Is(e, errors.New("error")))
465 }
466
467 type fooErr struct{ str string }
468
469 func (e fooErr) Error() string {
470 return e.str
471 }
472
473 type barErr struct{ str string }
474
475 func (e barErr) Error() string {
476 return e.str
477 }
478
479 func TestErrorAs(t *testing.T) {
480 var e Error
481 fe := fooErr{str: "foo"}
482 e = append(e, fe)
483
484 var tf fooErr
485 var tb barErr
486
487 assert.True(t, errors.As(e, &tf))
488 assert.False(t, errors.As(e, &tb))
489 assert.Equal(t, "foo", tf.str)
490 }
491
492 func TestUnwrap(t *testing.T) {
493 testError := errors.New("test error")
494 err := Do(
495 func() error {
496 return testError
497 },
498 Attempts(1),
499 )
500
501 assert.Error(t, err)
502 assert.Equal(t, testError, errors.Unwrap(err))
503 }