The Wayback Machine - https://web.archive.org/web/20200916090538/https://github.com/kamilsk/retry
Skip to content
v5
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

♻️ retry

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

Build Documentation Quality Template Coverage Awesome

💡 Idea

The package based on Rican7/retry but fully reworked and focused on integration with the 🚧 breaker and the built-in context packages.

A full description of the idea is available here.

🏆 Motivation

I developed distributed systems at Lazada, and later at Avito, which communicate with each other through a network, and I need a package to make these communications more reliable.

🤼‍♂️ How to

Important: retry/v5 compatible with breaker version v1.2+ and above.

retry.Do

var response *http.Response

action := func(ctx context.Context) error {
	var err error
	req := http.NewRequestWithContext(
		ctx,
		http.MethodGet, "https://github.com/kamilsk/retry",
		nil,
	)
	response, err = http.DefaultClient.Do(req)
	return err
}

// you can combine multiple Breakers into one
interrupter := breaker.MultiplexTwo(
	breaker.BreakByTimeout(time.Minute),
	breaker.BreakBySignal(os.Interrupt),
)
defer interrupter.Close()

if err := retry.Do(interrupter, action, strategy.Limit(3)); err != nil {
	if err == breaker.Interrupted {
		// operation was interrupted
	}
	// handle error
}
// work with response

or use Context

ctx, cancel := context.WithTimeout(request.Context(), time.Minute)
defer cancel()

if err := retry.Do(ctx, action, strategy.Limit(3)); err != nil {
	if err == context.Canceled || err == context.DeadlineExceeded {
		// operation was interrupted
	}
	// handle error
}
// work with response

Complex example

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"math/rand"
	"net"
	"time"

	"github.com/kamilsk/retry/v5"
	"github.com/kamilsk/retry/v5/backoff"
	"github.com/kamilsk/retry/v5/jitter"
	"github.com/kamilsk/retry/v5/strategy"
)

func main() {
	what := SendRequest

	how := retry.How{
		strategy.Limit(5),
		strategy.BackoffWithJitter(
			backoff.Fibonacci(10*time.Millisecond),
			jitter.NormalDistribution(
				rand.New(rand.NewSource(time.Now().UnixNano())),
				0.25,
			),
		),
		strategy.CheckError(
			strategy.NetworkError(strategy.Skip),
			DatabaseError(),
		),
	}

	breaker, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	if err := retry.Do(breaker, what, how...); err != nil {
		log.Fatal(err)
	}
}

func SendRequest(ctx context.Context) error {
	// communicate with some service
}

func DatabaseError() func(error) bool {
	blacklist := []error{sql.ErrNoRows, sql.ErrConnDone, sql.ErrTxDone}
	return func(err error) bool {
		for _, preset := range blacklist {
			if err == preset {
				return false
			}
		}
		return true
	}
}

🧩 Integration

The library uses SemVer for versioning, and it is not BC-safe through major releases. You can use go modules to manage its version.

$ go get github.com/kamilsk/retry/v5@latest

🤲 Outcomes

Console tool for command execution with retries

This example shows how to repeat console command until successful.

$ retry -timeout 10m -backoff lin:500ms -- /bin/sh -c 'echo "trying..."; exit $((1 + RANDOM % 10 > 5))'

asciicast

See more details here.


made with ❤️ for everyone

You can’t perform that action at this time.