Functional mechanism based on context to perform actions repetitively until successful.
Differences from Rican7/retry
- Fixed bug with an unexpected infinite loop.
- Added a clear mechanism for this purpose as the Infinite strategy.
- Added
context
support to cancellation. - Added
error
transmission between attempts.- Added
classifier
to handle them (see classifier package).
- Added
- Added CLI tool
retry
which provides functionality for repeating terminal commands (see cmd/retry).
This example shows how to repeat console command until successful.
$ retry --infinite -timeout 10m -backoff=lin:500ms -- /bin/sh -c 'echo "trying..."; exit $((1 + RANDOM % 10 > 5))'
See more details here.
This example shows how to extend standard http.Client with retry under the hood.
type client struct {
base *http.Client
strategies []strategy.Strategy
}
func New(timeout time.Duration, strategies ...strategy.Strategy) *client {
return &client{
base: &http.Client{Timeout: timeout},
strategies: strategies,
}
}
func (c *client) Get(deadline <-chan struct{}, url string) (*http.Response, error) {
var response *http.Response
err := retry.Retry(deadline, func(uint) error {
resp, err := c.base.Get(url)
if err != nil {
return err
}
response = resp
return nil
}, c.strategies...)
return response, err
}
This example shows how to use retry to restore database connection by database/sql/driver.Pinger
.
MustOpen := func() *sql.DB {
db, err := sql.Open("stub", "stub://test")
if err != nil {
panic(err)
}
return db
}
go func(db *sql.DB, ctx context.Context, shutdown chan<- struct{}, frequency time.Duration,
strategies ...strategy.Strategy) {
defer func() {
if r := recover(); r != nil {
shutdown <- struct{}{}
}
}()
ping := func(uint) error {
return db.Ping()
}
for {
if err := retry.Retry(ctx.Done(), ping, strategies...); err != nil {
panic(err)
}
time.Sleep(frequency)
}
}(MustOpen(), context.Background(), shutdown, time.Millisecond, strategy.Limit(1))
This example shows how to use context and retry together.
communication := make(chan error)
go service.Listen(communication)
action := func(uint) error {
communication <- nil // ping
return <-communication // pong
}
ctx := retry.WithContext(context.Background(), retry.WithTimeout(time.Second))
if err := retry.Retry(ctx.Done(), action, strategy.Delay(time.Millisecond)); err != nil {
// the service does not respond within one second
}
See more details here.
interrupter := retry.Multiplex(
retry.WithTimeout(time.Second),
retry.WithSignal(os.Interrupt),
)
if err := retry.Retry(interrupter, func(uint) error { time.Sleep(time.Second); return nil }); err == nil {
panic("press Ctrl+C")
}
// successful interruption
$ go get github.com/kamilsk/retry
$ egg bitbucket.org/kamilsk/retry
egg is an
extended go get
.
This library is using SemVer for versioning, and it is not
BC-safe. Therefore, do not use go get -u
to update it,
use dep or something similar for this purpose.