Documentation ¶
Overview ¶
Package retry provides the most advanced interruptible mechanism to perform actions repetitively until successful. The retry based on https://github.com/Rican7/retry but fully reworked and focused on integration with the https://github.com/kamilsk/breaker and the built-in https://pkg.go.dev/context package.
Example ¶
package main import ( "context" "database/sql" "fmt" "math/rand" "net" "time" "github.com/kamilsk/retry/v5" "github.com/kamilsk/retry/v5/backoff" "github.com/kamilsk/retry/v5/exp" "github.com/kamilsk/retry/v5/jitter" "github.com/kamilsk/retry/v5/strategy" ) var generator = rand.New(rand.NewSource(0)) 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, ), ), // experimental exp.CheckError( exp.NetworkError(exp.Skip), DatabaseError(), ), } breaker, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() if err := retry.Do(breaker, what, how...); err != nil { panic(err) } fmt.Println("success communication") } func SendRequest(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() default: } if generator.Intn(5) > 3 { return &net.DNSError{Name: "unknown host", IsTemporary: true} } return nil } 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 } }
Output: success communication
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Do ¶
func Do( breaker Breaker, action func(context.Context) error, strategies ...func(Breaker, uint, error) bool, ) error
Do takes the action and performs it, repetitively, until successful.
Optionally, strategies may be passed that assess whether or not an attempt should be made.
Example (BadCases) ¶
The example shows the difference between Do and Go.
var ( realTime = 100 * time.Millisecond needTime = 5 * time.Millisecond ) { badAction := func(context.Context) error { time.Sleep(realTime) return nil } now := time.Now() breaker, cancel := context.WithTimeout(context.Background(), needTime) silent(retry.Do(breaker, badAction)) if time.Since(now) < realTime { fmt.Println("unexpected waiting time") } cancel() } { badStrategy := func(strategy.Breaker, uint, error) bool { time.Sleep(realTime) return true } now := time.Now() breaker, cancel := context.WithTimeout(context.Background(), needTime) silent(retry.Do(breaker, func(context.Context) error { return nil }, badStrategy)) if time.Since(now) < realTime { fmt.Println("unexpected waiting time") } cancel() } fmt.Println("done")
Output: done
func Go ¶
func Go( breaker Breaker, action func(context.Context) error, strategies ...func(Breaker, uint, error) bool, ) error
Go takes the action and performs it, repetitively, until successful. It differs from the Do method in that it performs the action in a goroutine.
Optionally, strategies may be passed that assess whether or not an attempt should be made.
Example (Guarantees) ¶
The example shows the difference between Do and Go.
var ( sleepTime = 100 * time.Millisecond needTime = 5 * time.Millisecond inaccuracy = time.Millisecond ) { badAction := func(context.Context) error { time.Sleep(sleepTime) return nil } now := time.Now() breaker, cancel := context.WithTimeout(context.Background(), needTime) silent(retry.Go(breaker, badAction)) if time.Since(now)-needTime > time.Millisecond+inaccuracy { fmt.Println("unexpected waiting time") } cancel() } { badStrategy := func(strategy.Breaker, uint, error) bool { time.Sleep(sleepTime) return true } now := time.Now() breaker, cancel := context.WithTimeout(context.Background(), needTime) silent(retry.Go(breaker, func(context.Context) error { return nil }, badStrategy)) if time.Since(now)-needTime > time.Millisecond+inaccuracy { fmt.Println("unexpected waiting time") } cancel() } fmt.Println("done")
Output: done
Types ¶
type Breaker ¶
type Breaker = interface { // Done returns a channel that's closed when a cancellation signal occurred. Done() <-chan struct{} // If Done is not yet closed, Err returns nil. // If Done is closed, Err returns a non-nil error. // After Err returns a non-nil error, successive calls to Err return the same error. Err() error }
A Breaker carries a cancellation signal to interrupt an action execution.
It is a subset of the built-in context and github.com/kamilsk/breaker interfaces.
Directories ¶
Path | Synopsis |
---|---|
Package backoff provides stateless methods of calculating durations based on a number of attempts made.
|
Package backoff provides stateless methods of calculating durations based on a number of attempts made. |
Package jitter provides methods of transforming durations.
|
Package jitter provides methods of transforming durations. |
Package strategy provides a way to define how retry is performed.
|
Package strategy provides a way to define how retry is performed. |