diff --git a/README.md b/README.md index 32941e8..8e5485f 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,16 @@ `go-again` **thread safely** wraps a given function and executes it until it returns a nil error or exceeds the maximum number of retries. The configuration consists of the maximum number of retries, the interval, a jitter to add a randomized backoff, the timeout, and a registry to store errors that you consider temporary, hence worth a retry. -The `Retry` method takes a context, a function, and an optional list of `temporary errors` as arguments. It supports cancellation from the context and a channel invoking the `Cancel()` function. -The returned type is `RetryErrors` which contains the list of errors returned at each attempt and the last error returned by the function. +The `Do` method takes a context, a function, and an optional list of `temporary errors` as arguments. It supports cancellation from the context and a channel invoking the `Cancel()` function. +The returned type is `Errors` which contains the list of errors returned at each attempt and the last error returned by the function. ```golang -// RetryErrors holds the error returned by the retry function along with the trace of each attempt. -type RetryErrors struct { +// Errors holds the error returned by the retry function along with the trace of each attempt. +type Errors struct { // Retries hold the trace of each attempt. Retries map[int]error - // ExitError holds the last error returned by the retry function. - ExitError error + // Last holds the last error returned by the retry function. + Last error } ``` @@ -29,7 +29,7 @@ The registry only allows you to retry a function if it returns a registered erro defer retrier.Registry.UnRegisterTemporaryError("http.ErrAbortHandler") var retryCount int - errs := retrier.Retry(context.TODO(), func() error { + errs := retrier.Do(context.TODO(), func() error { retryCount++ if retryCount < 3 { return http.ErrAbortHandler @@ -37,26 +37,26 @@ The registry only allows you to retry a function if it returns a registered erro return nil }, "http.ErrAbortHandler") - if errs.ExitError != nil { + if errs.Last != nil { // handle error } ``` -Should you retry regardless of the error returned, that's easy. It's enough calling the Retry function without passing a plausible set of registered error names: +Should you retry regardless of the error returned, that's easy. It's enough calling the Do function without passing a plausible set of registered error names: ```go var retryCount int retrier := again.NewRetrier(again.WithTimeout(1*time.Second), again.WithJitter(500*time.Millisecond), again.WithMaxRetries(3)) - errs := retrier.Retry(context.TODO(), func() error { + errs := retrier.Do(context.TODO(), func() error { retryCount++ if retryCount < 3 { return http.ErrAbortHandler } return nil }) - if errs.ExitError != nil { + if errs.Last != nil { // handle error } ``` @@ -121,11 +121,11 @@ func main() { }) // Retry a function. - errs := retrier.Retry(context.TODO(), func() error { + errs := retrier.Do(context.TODO(), func() error { // Do something here. return fmt.Errorf("temporary error") }, "temporary error") - if errs.ExitError != nil { + if errs.Last != nil { fmt.Println(err) } } diff --git a/examples/chan/chan.go b/examples/chan/chan.go index 408b845..788a085 100644 --- a/examples/chan/chan.go +++ b/examples/chan/chan.go @@ -30,8 +30,8 @@ func main() { } // Retry the function. - errs := retrier.Retry(ctx, fn) - if errs.ExitError != nil { + errs := retrier.Do(ctx, fn) + if errs.Last != nil { fmt.Println(errs) } else { fmt.Println("success") diff --git a/examples/context/context.go b/examples/context/context.go index ad9bbfc..8a5c8c5 100644 --- a/examples/context/context.go +++ b/examples/context/context.go @@ -30,9 +30,9 @@ func main() { } // Retry the function. - errs := retrier.Retry(ctx, fn) + errs := retrier.Do(ctx, fn) - if errs.ExitError != nil { + if errs.Last != nil { fmt.Println(errs) } else { fmt.Println("success") diff --git a/retry.go b/retry.go index 20777c1..a261302 100644 --- a/retry.go +++ b/retry.go @@ -9,10 +9,10 @@ import ( ) var ( - // retryErrorsPool is a pool of RetryErrors objects. - retryErrorsPool = sync.Pool{ + // errorsPool is a pool of Errors objects. + errorsPool = sync.Pool{ New: func() interface{} { - return &RetryErrors{ + return &Errors{ Retries: make(map[int]error), } }, @@ -22,12 +22,12 @@ var ( // RetryableFunc signature of retryable function type RetryableFunc func() error -// RetryErrors holds the error returned by the retry function along with the trace of each attempt. -type RetryErrors struct { +// Errors holds the error returned by the retry function along with the trace of each attempt. +type Errors struct { // Retries holds the trace of each attempt. Retries map[int]error - // ExitError holds the last error returned by the retry function. - ExitError error + // Last holds the last error returned by the retry function. + Last error } // Retrier is a type that retries a function until it returns a nil error or the maximum number of retries is reached. @@ -103,31 +103,31 @@ func (r *Retrier) SetRegistry(reg *registry) error { return nil } -// Retry retries a `retryableFunc` until it returns a nil error or the maximum number of retries is reached. -// - If the maximum number of retries is reached, the function returns a `RetryError` object. -// - If the `retryableFunc` returns a nil error, the function assigns a `RetryErrors.ExitError` before returning. +// Do retries a `retryableFunc` until it returns a nil error or the maximum number of retries is reached. +// - If the maximum number of retries is reached, the function returns an `Errors` object. +// - If the `retryableFunc` returns a nil error, the function assigns an `Errors.Last` before returning. // - If the `retryableFunc` returns a temporary error, the function retries the function. -// - If the `retryableFunc` returns a non-temporary error, the function assigns the error to `RetryErrors.ExitError` and returns. +// - If the `retryableFunc` returns a non-temporary error, the function assigns the error to `Errors.Last` and returns. // - If the `temporaryErrors` list is empty, the function retries the function until the maximum number of retries is reached. // - The context is used to cancel the retries, or set a deadline if the `retryableFunc` hangs. -func (r *Retrier) Retry(ctx context.Context, retryableFunc RetryableFunc, temporaryErrors ...string) (errs *RetryErrors) { +func (r *Retrier) Do(ctx context.Context, retryableFunc RetryableFunc, temporaryErrors ...string) (errs *Errors) { // lock the mutex to synchronize access to the timer. r.mutex.RLock() defer r.mutex.RUnlock() - // get a new RetryErrors object from the pool. - errs = retryErrorsPool.Get().(*RetryErrors) - defer retryErrorsPool.Put(errs) + // get a new Errors object from the pool. + errs = errorsPool.Get().(*Errors) + defer errorsPool.Put(errs) // If the maximum number of retries is 0, call the function once and return the result. if r.MaxRetries == 0 { - errs.ExitError = retryableFunc() + errs.Last = retryableFunc() return } // Check for invalid inputs. if retryableFunc == nil { - errs.ExitError = errors.New("failed to invoke the function. It appears to be is nil") + errs.Last = errors.New("failed to invoke the function. It appears to be is nil") return } @@ -143,7 +143,7 @@ func (r *Retrier) Retry(ctx context.Context, retryableFunc RetryableFunc, tempor // If the function returns a nil error, return nil. if r.err == nil { - errs.ExitError = nil + errs.Last = nil return } @@ -158,7 +158,7 @@ func (r *Retrier) Retry(ctx context.Context, retryableFunc RetryableFunc, tempor // Check if the context is cancelled. select { case <-ctx.Done(): - errs.ExitError = ctx.Err() + errs.Last = ctx.Err() return default: } @@ -172,11 +172,11 @@ func (r *Retrier) Retry(ctx context.Context, retryableFunc RetryableFunc, tempor select { case <-r.cancel: r.timer.put(timer) - errs.ExitError = errors.New("retries cancelled") + errs.Last = errors.New("retries cancelled") return case <-r.stop: r.timer.put(timer) - errs.ExitError = errors.New("retries stopped") + errs.Last = errors.New("retries stopped") return case <-timer.C: r.timer.put(timer) @@ -186,7 +186,7 @@ func (r *Retrier) Retry(ctx context.Context, retryableFunc RetryableFunc, tempor return } -// Cancel cancels the retries notifying the `Retry` function to return. +// Cancel cancels the retries notifying the `Do` function to return. func (r *Retrier) Cancel() { r.once.Do(func() { close(r.cancel) diff --git a/retry_test.go b/retry_test.go index 385c468..a94876e 100644 --- a/retry_test.go +++ b/retry_test.go @@ -17,7 +17,7 @@ func TestRetry(t *testing.T) { defer retrier.Registry.UnRegisterTemporaryError("http.ErrAbortHandler") - errs := retrier.Retry(context.TODO(), func() error { + errs := retrier.Do(context.TODO(), func() error { retryCount++ if retryCount < 3 { return http.ErrAbortHandler @@ -25,8 +25,8 @@ func TestRetry(t *testing.T) { return nil }, "http.ErrAbortHandler") - if errs.ExitError != nil { - t.Errorf("retry returned an unexpected error: %v", errs.ExitError) + if errs.Last != nil { + t.Errorf("retry returned an unexpected error: %v", errs.Last) } if retryCount != 3 { t.Errorf("retry did not retry the function the expected number of times. Got: %d, Expecting: %d", retryCount, 3) @@ -38,7 +38,7 @@ func TestWithoutRegistry(t *testing.T) { var retryCount int retrier := NewRetrier() - errs := retrier.Retry(context.TODO(), func() error { + errs := retrier.Do(context.TODO(), func() error { retryCount++ if retryCount < 3 { return http.ErrAbortHandler @@ -46,8 +46,8 @@ func TestWithoutRegistry(t *testing.T) { return nil }) - if errs.ExitError != nil { - t.Errorf("retry returned an unexpected error: %v", errs.ExitError) + if errs.Last != nil { + t.Errorf("retry returned an unexpected error: %v", errs.Last) } if retryCount != 3 { t.Errorf("retry did not retry the function the expected number of times. Got: %d, Expecting: %d", retryCount, 1) @@ -62,7 +62,7 @@ func TestRetryWithDefaults(t *testing.T) { defer retrier.Registry.Clean() - errs := retrier.Retry(context.TODO(), func() error { + errs := retrier.Do(context.TODO(), func() error { retryCount++ if retryCount < 3 { return http.ErrHandlerTimeout @@ -70,8 +70,8 @@ func TestRetryWithDefaults(t *testing.T) { return nil }, "http.ErrHandlerTimeout") - if errs.ExitError != nil { - t.Errorf("retry returned an unexpected error: %v", errs.ExitError) + if errs.Last != nil { + t.Errorf("retry returned an unexpected error: %v", errs.Last) } if retryCount != 3 { t.Errorf("retry did not retry the function the expected number of times. Got: %d, Expecting: %d", retryCount, 3) @@ -126,7 +126,7 @@ func TestRegistry(t *testing.T) { // } // return nil // } -// err := r.Retry(fn, "temporary error").(*RetryError) +// err := r.Retry(fn, "temporary error").(*Errors) // if err != nil || err.MaxRetries != 50 { // b.Errorf("retry returned an unexpected error: %v", err) // } @@ -149,7 +149,7 @@ func BenchmarkRetry(b *testing.B) { return nil } b.StartTimer() - r.Retry(context.TODO(), fn, "temporary error") + r.Do(context.TODO(), fn, "temporary error") b.StopTimer() } }