Home

Awesome

♻️ retry

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

Awesome Patreon Build Status Code Coverage Code Quality GoDoc Research License

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

egg<sup id="anchor-egg">1</sup> 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, glide or something similar for this purpose.

<sup id="egg">1</sup> The project is still in prototyping.


Gitter @kamilsk @octolab

made with ❤️ by OctoLab