fix: retry caldav init at startup

This commit is contained in:
Cyrille Nofficial 2022-09-28 09:02:31 +02:00
parent 352dfcf2b6
commit b8fde634b5
15 changed files with 976 additions and 3 deletions

1
go.mod
View File

@ -3,6 +3,7 @@ module domogeek
go 1.18
require (
github.com/avast/retry-go v2.7.0+incompatible
github.com/dolanor/caldav-go v0.2.1
github.com/hellofresh/health-go/v4 v4.5.0
github.com/prometheus/client_golang v1.12.1

2
go.sum
View File

@ -39,6 +39,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/avast/retry-go v2.7.0+incompatible h1:XaGnzl7gESAideSjr+I8Hki/JBi+Yb9baHlMRPeSC84=
github.com/avast/retry-go v2.7.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=

View File

@ -2,6 +2,7 @@ package calendar
import (
"fmt"
"github.com/avast/retry-go"
"github.com/dolanor/caldav-go/caldav"
"github.com/dolanor/caldav-go/caldav/entities"
"github.com/dolanor/caldav-go/icalendar/components"
@ -28,10 +29,25 @@ func NewCaldav(caldavUrl, caldavPath string) (Caldav, error) {
server, _ := caldav.NewServer(caldavUrl)
// create a CalDAV client to speak to the server
var client = caldav.NewClient(server, http.DefaultClient)
err := retry.Do(
func() error {
// start executing requests!
err := client.ValidateServer(caldavPath)
if err != nil {
return nil, fmt.Errorf("bad caldav configuration, unable to validate connexion: %w", err)
return fmt.Errorf("bad caldav configuration, unable to validate connexion: %w", err)
}
return nil
},
retry.OnRetry(
func(n uint, err error) {
zap.S().Errorf("unable to validate caldav connection on retry %d: %v", n, err)
},
),
retry.Attempts(0), // Infinite attempts
retry.DelayType(retry.BackOffDelay),
)
if err != nil {
return nil, fmt.Errorf("unable to validate caldav connection: %w", err)
}
return client, nil
}

21
vendor/github.com/avast/retry-go/.gitignore generated vendored Normal file
View File

@ -0,0 +1,21 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
# dep
vendor/
Gopkg.lock
# cover
coverage.txt

37
vendor/github.com/avast/retry-go/.godocdown.tmpl generated vendored Normal file
View File

@ -0,0 +1,37 @@
# {{ .Name }}
[![Release](https://img.shields.io/github/release/avast/retry-go.svg?style=flat-square)](https://github.com/avast/retry-go/releases/latest)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Travis](https://img.shields.io/travis/avast/retry-go.svg?style=flat-square)](https://travis-ci.org/avast/retry-go)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/fieg9gon3qlq0a9a?svg=true)](https://ci.appveyor.com/project/JaSei/retry-go)
[![Go Report Card](https://goreportcard.com/badge/github.com/avast/retry-go?style=flat-square)](https://goreportcard.com/report/github.com/avast/retry-go)
[![GoDoc](https://godoc.org/github.com/avast/retry-go?status.svg&style=flat-square)](http://godoc.org/github.com/avast/retry-go)
[![codecov.io](https://codecov.io/github/avast/retry-go/coverage.svg?branch=master)](https://codecov.io/github/avast/retry-go?branch=master)
[![Sourcegraph](https://sourcegraph.com/github.com/avast/retry-go/-/badge.svg)](https://sourcegraph.com/github.com/avast/retry-go?badge)
{{ .EmitSynopsis }}
{{ .EmitUsage }}
## Contributing
Contributions are very much welcome.
### Makefile
Makefile provides several handy rules, like README.md `generator` , `setup` for prepare build/dev environment, `test`, `cover`, etc...
Try `make help` for more information.
### Before pull request
please try:
* run tests (`make test`)
* run linter (`make lint`)
* if your IDE don't automaticaly do `go fmt`, run `go fmt` (`make fmt`)
### README
README.md are generate from template [.godocdown.tmpl](.godocdown.tmpl) and code documentation via [godocdown](https://github.com/robertkrimen/godocdown).
Never edit README.md direct, because your change will be lost.

21
vendor/github.com/avast/retry-go/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,21 @@
language: go
go:
- 1.7
- 1.8
- 1.9
- "1.10"
- 1.11
- 1.12
- 1.13
- 1.14
- 1.15
install:
- make setup
script:
- make ci
after_success:
- bash <(curl -s https://codecov.io/bash)

3
vendor/github.com/avast/retry-go/Gopkg.toml generated vendored Normal file
View File

@ -0,0 +1,3 @@
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.1.4"

21
vendor/github.com/avast/retry-go/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Avast
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

65
vendor/github.com/avast/retry-go/Makefile generated vendored Normal file
View File

@ -0,0 +1,65 @@
SOURCE_FILES?=$$(go list ./... | grep -v /vendor/)
TEST_PATTERN?=.
TEST_OPTIONS?=
DEP?=$$(which dep)
VERSION?=$$(cat VERSION)
LINTER?=$$(which golangci-lint)
LINTER_VERSION=1.15.0
ifeq ($(OS),Windows_NT)
DEP_VERS=dep-windows-amd64
LINTER_FILE=golangci-lint-$(LINTER_VERSION)-windows-amd64.zip
LINTER_UNPACK= >| app.zip; unzip -j app.zip -d $$GOPATH/bin; rm app.zip
else ifeq ($(OS), Darwin)
LINTER_FILE=golangci-lint-$(LINTER_VERSION)-darwin-amd64.tar.gz
LINTER_UNPACK= | tar xzf - -C $$GOPATH/bin --wildcards --strip 1 "**/golangci-lint"
else
DEP_VERS=dep-linux-amd64
LINTER_FILE=golangci-lint-$(LINTER_VERSION)-linux-amd64.tar.gz
LINTER_UNPACK= | tar xzf - -C $$GOPATH/bin --wildcards --strip 1 "**/golangci-lint"
endif
setup:
go get -u github.com/pierrre/gotestcover
go get -u golang.org/x/tools/cmd/cover
go get -u github.com/robertkrimen/godocdown/godocdown
@if [ "$(LINTER)" = "" ]; then\
curl -L https://github.com/golangci/golangci-lint/releases/download/v$(LINTER_VERSION)/$(LINTER_FILE) $(LINTER_UNPACK) ;\
chmod +x $$GOPATH/bin/golangci-lint;\
fi
@if [ "$(DEP)" = "" ]; then\
curl -L https://github.com/golang/dep/releases/download/v0.3.1/$(DEP_VERS) >| $$GOPATH/bin/dep;\
chmod +x $$GOPATH/bin/dep;\
fi
dep ensure
generate: ## Generate README.md
godocdown >| README.md
test: generate test_and_cover_report lint
test_and_cover_report:
gotestcover $(TEST_OPTIONS) -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m
cover: test ## Run all the tests and opens the coverage report
go tool cover -html=coverage.txt
fmt: ## gofmt and goimports all go files
find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
lint: ## Run all the linters
golangci-lint run
ci: test_and_cover_report ## Run all the tests but no linters - use https://golangci.com integration instead
build:
go build
release: ## Release new version
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)
# Absolutely awesome: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.DEFAULT_GOAL := build

351
vendor/github.com/avast/retry-go/README.md generated vendored Normal file
View File

@ -0,0 +1,351 @@
# retry
[![Release](https://img.shields.io/github/release/avast/retry-go.svg?style=flat-square)](https://github.com/avast/retry-go/releases/latest)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Travis](https://img.shields.io/travis/avast/retry-go.svg?style=flat-square)](https://travis-ci.org/avast/retry-go)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/fieg9gon3qlq0a9a?svg=true)](https://ci.appveyor.com/project/JaSei/retry-go)
[![Go Report Card](https://goreportcard.com/badge/github.com/avast/retry-go?style=flat-square)](https://goreportcard.com/report/github.com/avast/retry-go)
[![GoDoc](https://godoc.org/github.com/avast/retry-go?status.svg&style=flat-square)](http://godoc.org/github.com/avast/retry-go)
[![codecov.io](https://codecov.io/github/avast/retry-go/coverage.svg?branch=master)](https://codecov.io/github/avast/retry-go?branch=master)
[![Sourcegraph](https://sourcegraph.com/github.com/avast/retry-go/-/badge.svg)](https://sourcegraph.com/github.com/avast/retry-go?badge)
Simple library for retry mechanism
slightly inspired by
[Try::Tiny::Retry](https://metacpan.org/pod/Try::Tiny::Retry)
### SYNOPSIS
http get with retry:
url := "http://example.com"
var body []byte
err := retry.Do(
func() error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
return nil
},
)
fmt.Println(body)
[next examples](https://github.com/avast/retry-go/tree/master/examples)
### SEE ALSO
* [giantswarm/retry-go](https://github.com/giantswarm/retry-go) - slightly
complicated interface.
* [sethgrid/pester](https://github.com/sethgrid/pester) - only http retry for
http calls with retries and backoff
* [cenkalti/backoff](https://github.com/cenkalti/backoff) - Go port of the
exponential backoff algorithm from Google's HTTP Client Library for Java. Really
complicated interface.
* [rafaeljesus/retry-go](https://github.com/rafaeljesus/retry-go) - looks good,
slightly similar as this package, don't have 'simple' `Retry` method
* [matryer/try](https://github.com/matryer/try) - very popular package,
nonintuitive interface (for me)
### BREAKING CHANGES
1.0.2 -> 2.0.0
* argument of `retry.Delay` is final delay (no multiplication by `retry.Units`
anymore)
* function `retry.Units` are removed
* [more about this breaking change](https://github.com/avast/retry-go/issues/7)
0.3.0 -> 1.0.0
* `retry.Retry` function are changed to `retry.Do` function
* `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are
now implement via functions produces Options (aka `retry.OnRetry`)
## Usage
```go
var (
DefaultAttempts = uint(10)
DefaultDelay = 100 * time.Millisecond
DefaultMaxJitter = 100 * time.Millisecond
DefaultOnRetry = func(n uint, err error) {}
DefaultRetryIf = IsRecoverable
DefaultDelayType = CombineDelay(BackOffDelay, RandomDelay)
DefaultLastErrorOnly = false
DefaultContext = context.Background()
)
```
#### func BackOffDelay
```go
func BackOffDelay(n uint, config *Config) time.Duration
```
BackOffDelay is a DelayType which increases delay between consecutive retries
#### func Do
```go
func Do(retryableFunc RetryableFunc, opts ...Option) error
```
#### func FixedDelay
```go
func FixedDelay(_ uint, config *Config) time.Duration
```
FixedDelay is a DelayType which keeps delay the same through all iterations
#### func IsRecoverable
```go
func IsRecoverable(err error) bool
```
IsRecoverable checks if error is an instance of `unrecoverableError`
#### func RandomDelay
```go
func RandomDelay(_ uint, config *Config) time.Duration
```
RandomDelay is a DelayType which picks a random delay up to config.maxJitter
#### func Unrecoverable
```go
func Unrecoverable(err error) error
```
Unrecoverable wraps an error in `unrecoverableError` struct
#### type Config
```go
type Config struct {
}
```
#### type DelayTypeFunc
```go
type DelayTypeFunc func(n uint, config *Config) time.Duration
```
#### func CombineDelay
```go
func CombineDelay(delays ...DelayTypeFunc) DelayTypeFunc
```
CombineDelay is a DelayType the combines all of the specified delays into a new
DelayTypeFunc
#### type Error
```go
type Error []error
```
Error type represents list of errors in retry
#### func (Error) Error
```go
func (e Error) Error() string
```
Error method return string representation of Error It is an implementation of
error interface
#### func (Error) WrappedErrors
```go
func (e Error) WrappedErrors() []error
```
WrappedErrors returns the list of errors that this Error is wrapping. It is an
implementation of the `errwrap.Wrapper` interface in package
[errwrap](https://github.com/hashicorp/errwrap) so that `retry.Error` can be
used with that library.
#### type OnRetryFunc
```go
type OnRetryFunc func(n uint, err error)
```
Function signature of OnRetry function n = count of attempts
#### type Option
```go
type Option func(*Config)
```
Option represents an option for retry.
#### func Attempts
```go
func Attempts(attempts uint) Option
```
Attempts set count of retry default is 10
#### func Context
```go
func Context(ctx context.Context) Option
```
Context allow to set context of retry default are Background context
example of immediately cancellation (maybe it isn't the best example, but it describes behavior enough; I hope)
ctx, cancel := context.WithCancel(context.Background())
cancel()
retry.Do(
func() error {
...
},
retry.Context(ctx),
)
#### func Delay
```go
func Delay(delay time.Duration) Option
```
Delay set delay between retry default is 100ms
#### func DelayType
```go
func DelayType(delayType DelayTypeFunc) Option
```
DelayType set type of the delay between retries default is BackOff
#### func LastErrorOnly
```go
func LastErrorOnly(lastErrorOnly bool) Option
```
return the direct last error that came from the retried function default is
false (return wrapped errors with everything)
#### func MaxDelay
```go
func MaxDelay(maxDelay time.Duration) Option
```
MaxDelay set maximum delay between retry does not apply by default
#### func MaxJitter
```go
func MaxJitter(maxJitter time.Duration) Option
```
MaxJitter sets the maximum random Jitter between retries for RandomDelay
#### func OnRetry
```go
func OnRetry(onRetry OnRetryFunc) Option
```
OnRetry function callback are called each retry
log each retry example:
retry.Do(
func() error {
return errors.New("some error")
},
retry.OnRetry(func(n uint, err error) {
log.Printf("#%d: %s\n", n, err)
}),
)
#### func RetryIf
```go
func RetryIf(retryIf RetryIfFunc) Option
```
RetryIf controls whether a retry should be attempted after an error (assuming
there are any retry attempts remaining)
skip retry if special error example:
retry.Do(
func() error {
return errors.New("special error")
},
retry.RetryIf(func(err error) bool {
if err.Error() == "special error" {
return false
}
return true
})
)
By default RetryIf stops execution if the error is wrapped using
`retry.Unrecoverable`, so above example may also be shortened to:
retry.Do(
func() error {
return retry.Unrecoverable(errors.New("special error"))
}
)
#### type RetryIfFunc
```go
type RetryIfFunc func(error) bool
```
Function signature of retry if function
#### type RetryableFunc
```go
type RetryableFunc func() error
```
Function signature of retryable function
## Contributing
Contributions are very much welcome.
### Makefile
Makefile provides several handy rules, like README.md `generator` , `setup` for prepare build/dev environment, `test`, `cover`, etc...
Try `make help` for more information.
### Before pull request
please try:
* run tests (`make test`)
* run linter (`make lint`)
* if your IDE don't automaticaly do `go fmt`, run `go fmt` (`make fmt`)
### README
README.md are generate from template [.godocdown.tmpl](.godocdown.tmpl) and code documentation via [godocdown](https://github.com/robertkrimen/godocdown).
Never edit README.md direct, because your change will be lost.

1
vendor/github.com/avast/retry-go/VERSION generated vendored Normal file
View File

@ -0,0 +1 @@
2.7.0

19
vendor/github.com/avast/retry-go/appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,19 @@
version: "{build}"
clone_folder: c:\Users\appveyor\go\src\github.com\avast\retry-go
#os: Windows Server 2012 R2
platform: x64
install:
- copy c:\MinGW\bin\mingw32-make.exe c:\MinGW\bin\make.exe
- set GOPATH=C:\Users\appveyor\go
- set PATH=%PATH%;c:\MinGW\bin
- set PATH=%PATH%;%GOPATH%\bin;c:\go\bin
- set GOBIN=%GOPATH%\bin
- go version
- go env
- make setup
build_script:
- make ci

193
vendor/github.com/avast/retry-go/options.go generated vendored Normal file
View File

@ -0,0 +1,193 @@
package retry
import (
"context"
"math"
"math/rand"
"time"
)
// Function signature of retry if function
type RetryIfFunc func(error) bool
// Function signature of OnRetry function
// n = count of attempts
type OnRetryFunc func(n uint, err error)
type DelayTypeFunc func(n uint, config *Config) time.Duration
type Config struct {
attempts uint
delay time.Duration
maxDelay time.Duration
maxJitter time.Duration
onRetry OnRetryFunc
retryIf RetryIfFunc
delayType DelayTypeFunc
lastErrorOnly bool
context context.Context
maxBackOffN uint
}
// Option represents an option for retry.
type Option func(*Config)
// return the direct last error that came from the retried function
// default is false (return wrapped errors with everything)
func LastErrorOnly(lastErrorOnly bool) Option {
return func(c *Config) {
c.lastErrorOnly = lastErrorOnly
}
}
// Attempts set count of retry
// default is 10
func Attempts(attempts uint) Option {
return func(c *Config) {
c.attempts = attempts
}
}
// Delay set delay between retry
// default is 100ms
func Delay(delay time.Duration) Option {
return func(c *Config) {
c.delay = delay
}
}
// MaxDelay set maximum delay between retry
// does not apply by default
func MaxDelay(maxDelay time.Duration) Option {
return func(c *Config) {
c.maxDelay = maxDelay
}
}
// MaxJitter sets the maximum random Jitter between retries for RandomDelay
func MaxJitter(maxJitter time.Duration) Option {
return func(c *Config) {
c.maxJitter = maxJitter
}
}
// DelayType set type of the delay between retries
// default is BackOff
func DelayType(delayType DelayTypeFunc) Option {
return func(c *Config) {
c.delayType = delayType
}
}
// BackOffDelay is a DelayType which increases delay between consecutive retries
func BackOffDelay(n uint, config *Config) time.Duration {
// 1 << 63 would overflow signed int64 (time.Duration), thus 62.
const max uint = 62
if config.maxBackOffN == 0 {
if config.delay <= 0 {
config.delay = 1
}
config.maxBackOffN = max - uint(math.Floor(math.Log2(float64(config.delay))))
}
if n > config.maxBackOffN {
n = config.maxBackOffN
}
return config.delay << n
}
// FixedDelay is a DelayType which keeps delay the same through all iterations
func FixedDelay(_ uint, config *Config) time.Duration {
return config.delay
}
// RandomDelay is a DelayType which picks a random delay up to config.maxJitter
func RandomDelay(_ uint, config *Config) time.Duration {
return time.Duration(rand.Int63n(int64(config.maxJitter)))
}
// CombineDelay is a DelayType the combines all of the specified delays into a new DelayTypeFunc
func CombineDelay(delays ...DelayTypeFunc) DelayTypeFunc {
const maxInt64 = uint64(math.MaxInt64)
return func(n uint, config *Config) time.Duration {
var total uint64
for _, delay := range delays {
total += uint64(delay(n, config))
if total > maxInt64 {
total = maxInt64
}
}
return time.Duration(total)
}
}
// OnRetry function callback are called each retry
//
// log each retry example:
//
// retry.Do(
// func() error {
// return errors.New("some error")
// },
// retry.OnRetry(func(n uint, err error) {
// log.Printf("#%d: %s\n", n, err)
// }),
// )
func OnRetry(onRetry OnRetryFunc) Option {
return func(c *Config) {
c.onRetry = onRetry
}
}
// RetryIf controls whether a retry should be attempted after an error
// (assuming there are any retry attempts remaining)
//
// skip retry if special error example:
//
// retry.Do(
// func() error {
// return errors.New("special error")
// },
// retry.RetryIf(func(err error) bool {
// if err.Error() == "special error" {
// return false
// }
// return true
// })
// )
//
// By default RetryIf stops execution if the error is wrapped using `retry.Unrecoverable`,
// so above example may also be shortened to:
//
// retry.Do(
// func() error {
// return retry.Unrecoverable(errors.New("special error"))
// }
// )
func RetryIf(retryIf RetryIfFunc) Option {
return func(c *Config) {
c.retryIf = retryIf
}
}
// Context allow to set context of retry
// default are Background context
//
// example of immediately cancellation (maybe it isn't the best example, but it describes behavior enough; I hope)
// ctx, cancel := context.WithCancel(context.Background())
// cancel()
//
// retry.Do(
// func() error {
// ...
// },
// retry.Context(ctx),
// )
func Context(ctx context.Context) Option {
return func(c *Config) {
c.context = ctx
}
}

219
vendor/github.com/avast/retry-go/retry.go generated vendored Normal file
View File

@ -0,0 +1,219 @@
/*
Simple library for retry mechanism
slightly inspired by [Try::Tiny::Retry](https://metacpan.org/pod/Try::Tiny::Retry)
SYNOPSIS
http get with retry:
url := "http://example.com"
var body []byte
err := retry.Do(
func() error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
return nil
},
)
fmt.Println(body)
[next examples](https://github.com/avast/retry-go/tree/master/examples)
SEE ALSO
* [giantswarm/retry-go](https://github.com/giantswarm/retry-go) - slightly complicated interface.
* [sethgrid/pester](https://github.com/sethgrid/pester) - only http retry for http calls with retries and backoff
* [cenkalti/backoff](https://github.com/cenkalti/backoff) - Go port of the exponential backoff algorithm from Google's HTTP Client Library for Java. Really complicated interface.
* [rafaeljesus/retry-go](https://github.com/rafaeljesus/retry-go) - looks good, slightly similar as this package, don't have 'simple' `Retry` method
* [matryer/try](https://github.com/matryer/try) - very popular package, nonintuitive interface (for me)
BREAKING CHANGES
1.0.2 -> 2.0.0
* argument of `retry.Delay` is final delay (no multiplication by `retry.Units` anymore)
* function `retry.Units` are removed
* [more about this breaking change](https://github.com/avast/retry-go/issues/7)
0.3.0 -> 1.0.0
* `retry.Retry` function are changed to `retry.Do` function
* `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are now implement via functions produces Options (aka `retry.OnRetry`)
*/
package retry
import (
"context"
"fmt"
"strings"
"time"
)
// Function signature of retryable function
type RetryableFunc func() error
var (
DefaultAttempts = uint(10)
DefaultDelay = 100 * time.Millisecond
DefaultMaxJitter = 100 * time.Millisecond
DefaultOnRetry = func(n uint, err error) {}
DefaultRetryIf = IsRecoverable
DefaultDelayType = CombineDelay(BackOffDelay, RandomDelay)
DefaultLastErrorOnly = false
DefaultContext = context.Background()
)
func Do(retryableFunc RetryableFunc, opts ...Option) error {
var n uint
//default
config := &Config{
attempts: DefaultAttempts,
delay: DefaultDelay,
maxJitter: DefaultMaxJitter,
onRetry: DefaultOnRetry,
retryIf: DefaultRetryIf,
delayType: DefaultDelayType,
lastErrorOnly: DefaultLastErrorOnly,
context: DefaultContext,
}
//apply opts
for _, opt := range opts {
opt(config)
}
if err := config.context.Err(); err != nil {
return err
}
var errorLog Error
if !config.lastErrorOnly {
errorLog = make(Error, config.attempts)
} else {
errorLog = make(Error, 1)
}
lastErrIndex := n
for n < config.attempts {
err := retryableFunc()
if err != nil {
errorLog[lastErrIndex] = unpackUnrecoverable(err)
if !config.retryIf(err) {
break
}
config.onRetry(n, err)
// if this is last attempt - don't wait
if n == config.attempts-1 {
break
}
delayTime := config.delayType(n, config)
if config.maxDelay > 0 && delayTime > config.maxDelay {
delayTime = config.maxDelay
}
select {
case <-time.After(delayTime):
case <-config.context.Done():
return config.context.Err()
}
} else {
return nil
}
n++
if !config.lastErrorOnly {
lastErrIndex = n
}
}
if config.lastErrorOnly {
return errorLog[lastErrIndex]
}
return errorLog
}
// Error type represents list of errors in retry
type Error []error
// Error method return string representation of Error
// It is an implementation of error interface
func (e Error) Error() string {
logWithNumber := make([]string, lenWithoutNil(e))
for i, l := range e {
if l != nil {
logWithNumber[i] = fmt.Sprintf("#%d: %s", i+1, l.Error())
}
}
return fmt.Sprintf("All attempts fail:\n%s", strings.Join(logWithNumber, "\n"))
}
func lenWithoutNil(e Error) (count int) {
for _, v := range e {
if v != nil {
count++
}
}
return
}
// WrappedErrors returns the list of errors that this Error is wrapping.
// It is an implementation of the `errwrap.Wrapper` interface
// in package [errwrap](https://github.com/hashicorp/errwrap) so that
// `retry.Error` can be used with that library.
func (e Error) WrappedErrors() []error {
return e
}
type unrecoverableError struct {
error
}
// Unrecoverable wraps an error in `unrecoverableError` struct
func Unrecoverable(err error) error {
return unrecoverableError{err}
}
// IsRecoverable checks if error is an instance of `unrecoverableError`
func IsRecoverable(err error) bool {
_, isUnrecoverable := err.(unrecoverableError)
return !isUnrecoverable
}
func unpackUnrecoverable(err error) error {
if unrecoverable, isUnrecoverable := err.(unrecoverableError); isUnrecoverable {
return unrecoverable.error
}
return err
}

3
vendor/modules.txt vendored
View File

@ -1,3 +1,6 @@
# github.com/avast/retry-go v2.7.0+incompatible
## explicit
github.com/avast/retry-go
# github.com/beorn7/perks v1.0.1
## explicit; go 1.11
github.com/beorn7/perks/quantile