♻️ Functional mechanism based on channels to perform actions repetitively until successful.
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github update checklist May 5, 2018
backoff fix golint warnings Nov 30, 2017
classifier fix issue #73: convert tests to blackbox tests Nov 30, 2017
cmd update readme Sep 22, 2018
jitter fix golint warnings Nov 30, 2017
makes update .github, makes, travis and scrutinizer configs May 5, 2018
strategy fix golint warnings Nov 30, 2017
.gitattributes sync with kamilsk/semaphore Oct 29, 2017
.gitignore update .github, makes, travis and scrutinizer configs May 5, 2018
.goreleaser.yml fix #110: add quick start; fix #111: add scrutinizer config; fix #112:… Feb 18, 2018
.scrutinizer.yml update #119: extend exlude paths in scrutinizer config May 6, 2018
.travis.yml fix #119: fix builds on go 1.5 and 1.6 May 6, 2018
LICENSE fix #110: add quick start; fix #111: add scrutinizer config; fix #112:… Feb 18, 2018
Makefile update .github, makes, travis and scrutinizer configs May 5, 2018
README.md update from dev Oct 6, 2018
channel.go fix #115: hold vendor dir, simplify travis and scrutinizer config Mar 6, 2018
channel_test.go fix #119: fix builds on go 1.5 and 1.6 May 6, 2018
context.go fix #115: hold vendor dir, simplify travis and scrutinizer config Mar 6, 2018
context_test.go fix #115: hold vendor dir, simplify travis and scrutinizer config Mar 6, 2018
example_db_test.go fix issue #65: add example how to control database connection Nov 30, 2017
example_http_client_test.go fix #110: add quick start; fix #111: add scrutinizer config; fix #112:… Feb 18, 2018
example_retry_with_context_test.go fix issue #64: add http client example Nov 30, 2017
example_test.go fix issue #82: remove package context Oct 10, 2017
retry.go fix scrutinizer recommendations Oct 6, 2018
retry_test.go fix issue #123: fix critical runtime fault with unhandled panic in Re… Oct 6, 2018

README.md

♻️ retry Tweet

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

Awesome Patreon Build Status Code Coverage Code Quality GoDoc Research 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
}

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
$ # or use mirror
$ egg bitbucket.org/kamilsk/retry

egg1 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.

1 The project is still in prototyping.


Gitter @kamilsk @octolab

made with ❤️ by OctoLab