-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7c3d2ce
Showing
7 changed files
with
890 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
}) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
}) | ||
} |
Oops, something went wrong.