♻️ Functional mechanism based on channels to perform actions repetitively until successful.
Go Makefile
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
.github
backoff
classifier
cmd
jitter
makes
strategy
.gitattributes
.gitignore
.goreleaser.yml
.scrutinizer.yml
.travis.yml
LICENSE
Makefile
README.md
channel.go
channel_test.go
context.go
context_test.go
example_db_test.go
example_http_client_test.go
example_retry_with_context_test.go
example_test.go
retry.go
retry_test.go

README.md

retry Tweet

Analytics Functional mechanism based on channels to perform actions repetitively until successful.

Awesome Patreon Build Status Go Report Card Coverage Status GoDoc License

Differences from Rican7/retry

  • Fixed bug with an unexpected infinite loop.
    • Added a clear mechanism for this purpose as the Infinite strategy.
  • Added support of cancellation (based on simple channel, e.g. context.Done).
    • Made honest Action execution.
  • Added error transmission between attempts.
    • Added classifier to handle them (see classifier package).
  • Added CLI tool retry which provides functionality for repeating terminal commands (see cmd/retry).

Usage

Quick start

var (
	response *http.Response
	action   retry.Action = func(_ uint) error {
		var err error
		response, err = http.Get("https://github.com/kamilsk/retry")
		return err
	}
)

if err := retry.Retry(retry.WithTimeout(time.Minute), action, strategy.Limit(10)); err != nil {
	// handle error
	return
}
// work with response

Console tool for command execution with retries

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))'

asciicast

See more details here.

Create HTTP client with retry

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
}

Control database connection

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))

Use context for cancellation

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.

Interrupt execution

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

Installation

$ go get github.com/kamilsk/retry

Mirror

$ egg bitbucket.org/kamilsk/retry

egg is an extended go get.

Update

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.

Notes

  • research
  • tested on Go 1.5, 1.6, 1.7, 1.8, 1.9 and 1.10

Gitter @kamilsk @octolab

made with ❤️ by OctoLab