Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
minitauros committed Nov 10, 2021
0 parents commit 7c3d2ce
Show file tree
Hide file tree
Showing 7 changed files with 890 additions and 0 deletions.
109 changes: 109 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
The retry package provides functions for retrying any type of action a given number of times.

## Regular retry functions

### Retry()

```go
err := Retry(3, func() error {
err := someFunc()
if err != nil {
// Retry.
// If 4th attempt (max number of attempts exceeded),
// this error is returned.
return err
}
return nil // Stop retrying.
})
```

### RetryCtx()

```go
ctx, cancel := context.WithCancel(context.Background())

// Cancel the context.
// The function will never be called and
// the context error will be returned right away.
cancel()

err := RetryCtx(ctx, 3, func() error {
err := someFunc()
if err != nil {
// Retry.
// If 4th attempt (max number of attempts exceeded),
// this error is returned.
return err
}
return nil // Stop retrying.
})
```

### RetryWithStop()

If you want to have fine grained control over when the retrying should stop.

```go
err := RetryWithStop(3, func(stop func()) error {
err := someFunc()
if err != nil {
if _, ok := err.(SomeErr); ok {
stop() // Don't retry this type of error.
return err // Return this error.
}

return err // Retry.
}
stop() // No err was returned. We can stop retrying.
return nil // Retry.
})
```

### RetryWithStopCtx()

```go
ctx, cancel := context.WithCancel(context.Background())

// Cancel the context.
// The function will never be called and
// the context error will be returned right away.
cancel()

err := RetryWithStopCtx(ctx, 3, func(stop func()) error {
err := someFunc()
if err != nil {
if _, ok := err.(SomeErr); ok {
stop() // Don't retry this type of error.
return err // Return this error.
}

return err // Retry.
}
stop() // No err was returned. We can stop retrying.
return nil // Retry.
})
```

## Retry with backoff

Works the same as the regular retry functions, but sleeps before making a new attempt.

Example with the regular `Retry()` function:

```go
retrier := NewBackOffRetrier(time.Second, 2)
err := retrier.Retry(3, func() error {
err := someFunc()
if err != nil {
// Retry.
// First attempt will sleep for time.Second.
// Second attempt will sleep for time.Second * 2.
// Third attempt will sleep for time.Second * 2 * 2.
// Etc.
// If 4th attempt (max number of attempts exceeded),
// this error is returned.
return err
}
return nil // Stop retrying.
})
```
93 changes: 93 additions & 0 deletions backoff_retrier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package retry

import (
"context"
"math"
"time"
)

// Retrier is a function for retrying the given callback at max the given number of times.
// stop must be called to stop retrying.
type Retrier func(numTimes int, cb func(stop func()) error) error

// RetrierCtx is a function for retrying the given callback at max the given number of times.
// stop must be called to stop retrying.
type RetrierCtx func(ctx context.Context, numTimes int, cb func(stop func()) error) error

// BackOffRetrier retries a given callback, backing off on failure.
type BackOffRetrier struct {
initialDelay time.Duration
backOffCoefficient float64
}

// NewBackOffRetrier returns a new back off retrier.
func NewBackOffRetrier(initialDelay time.Duration, backOffCoefficient float64) *BackOffRetrier {
return &BackOffRetrier{initialDelay: initialDelay, backOffCoefficient: backOffCoefficient}
}

// Retry retries the given callback at max the given number of times.
// It stops as soon as a `nil` error is returned.
func (r *BackOffRetrier) Retry(numTimes int, cb func() error) error {
delay := r.initialDelay
return Retry(numTimes, func() error {
err := cb()
if err != nil {
time.Sleep(delay)
delay = time.Duration(math.Round(r.backOffCoefficient * float64(delay)))
}
return err
})
}

// RetryCtx retries the given callback at max the given number of times.
// It stops as soon as a `nil` error is returned.
func (r *BackOffRetrier) RetryCtx(ctx context.Context, numTimes int, cb func() error) error {
delay := r.initialDelay
return Retry(numTimes, func() error {
err := ctx.Err()
if err != nil {
return err
}

err = cb()
if err != nil {
time.Sleep(delay)
delay = time.Duration(math.Round(r.backOffCoefficient * float64(delay)))
}
return err
})
}

// RetryWithStop retries the given callback at max the given number of times.
// It stops only when `stop` is called.
func (r *BackOffRetrier) RetryWithStop(numTimes int, cb func(stop func()) error) error {
delay := r.initialDelay
return RetryWithStop(numTimes, func(stop func()) error {
err := cb(stop)
if err != nil {
time.Sleep(delay)
delay = time.Duration(math.Round(r.backOffCoefficient * float64(delay)))
}
return err
})
}

// RetryWithStopCtx retries the given callback at max the given number of times.
// It stops only when `stop` is called.
func (r *BackOffRetrier) RetryWithStopCtx(ctx context.Context, numTimes int, cb func(stop func()) error) error {
delay := r.initialDelay
return RetryWithStop(numTimes, func(stop func()) error {
err := ctx.Err()
if err != nil {
stop()
return err
}

err = cb(stop)
if err != nil {
time.Sleep(delay)
delay = time.Duration(math.Round(r.backOffCoefficient * float64(delay)))
}
return err
})
}
Loading

0 comments on commit 7c3d2ce

Please sign in to comment.