Skip to content
/ retry Public

♻️ The most advanced interruptible mechanism to perform actions repetitively until successful.

License

Notifications You must be signed in to change notification settings

kamilsk/retry

Folders and files

NameName
Last commit message
Last commit date

Latest commit

168fa9d · Feb 18, 2018
Dec 19, 2017
Nov 30, 2017
Nov 30, 2017
Feb 18, 2018
Nov 30, 2017
Oct 29, 2017
Nov 30, 2017
Oct 29, 2017
Feb 18, 2018
Feb 18, 2018
Feb 18, 2018
Feb 18, 2018
Feb 18, 2018
Dec 19, 2017
Feb 18, 2018
Feb 18, 2018
Oct 10, 2017
Feb 18, 2018
Oct 10, 2017
Nov 30, 2017
Feb 18, 2018
Nov 30, 2017
Oct 10, 2017
Dec 21, 2017
Nov 30, 2017

Repository files navigation

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(attempt 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.

Feedback

Gitter @kamilsk @octolab

Notes

  • research
  • tested on Go 1.5, 1.6, 1.7, 1.8, 1.9 and 1.10
  • made with ❤️ by OctoLab