Documentation
¶
Overview ¶
Package retryx provides a configurable retry engine with exponential backoff, jitter, and structured error reporting for industrial Go services.
The caller supplies a function that returns an error; retryx re-executes it until it succeeds, the attempts are exhausted, or the context is cancelled. Each attempt receives a RetryController that exposes the current attempt number and an [RetryController.Abort] method to stop retrying early.
resp, err := retryx.Do(ctx, func(rc retryx.RetryController) (*Response, error) {
resp, err := client.Call(ctx, req)
if isPermError(err) {
rc.Abort()
}
return resp, err
},
retryx.WithMaxAttempts(5),
retryx.WithBackoff(200*time.Millisecond),
)
Retryability is determined by errx.Error.Retryable when the error is an *errx.Error, or by a custom WithRetryIf predicate. Errors that are not retryable stop the loop immediately.
Index ¶
Examples ¶
Constants ¶
const ( // CodeExhausted indicates all retry attempts have been exhausted. CodeExhausted = "EXHAUSTED" // CodeCancelled indicates the retry loop was cancelled via context. CodeCancelled = "CANCELLED" // CodeAborted indicates the caller explicitly aborted the retry loop. CodeAborted = "ABORTED" )
const DomainRetry = "RETRY"
DomainRetry is the errx domain for all retry errors.
Variables ¶
This section is empty.
Functions ¶
func Do ¶
func Do[T any](ctx context.Context, fn func(rc RetryController) (T, error), opts ...Option) (T, error)
Do executes fn repeatedly until it succeeds, the attempts are exhausted, the context is cancelled, or the caller calls [RetryController.Abort].
On success (fn returns a nil error), Do returns the value and nil.
On failure, Do returns zero T and a structured *errx.Error with one of:
- CodeExhausted: all attempts failed (wraps the last error)
- CodeCancelled: context was cancelled (wraps ctx.Err())
- CodeAborted: caller called Abort (wraps the last error)
If fn returns a non-retryable error (determined by WithRetryIf or errx.Error.Retryable), the loop stops immediately and returns CodeExhausted wrapping that error.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"time"
"github.com/aasyanov/urx/pkg/retryx"
)
func main() {
ctx := context.Background()
result, err := retryx.Do(ctx, func(rc retryx.RetryController) (string, error) {
if rc.Number() < 2 {
return "", errors.New("not ready")
}
return "done", nil
}, retryx.WithMaxAttempts(5), retryx.WithBackoff(10*time.Millisecond))
fmt.Println(result, err)
}
Output: done <nil>
Example (Abort) ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/aasyanov/urx/pkg/retryx"
)
func main() {
ctx := context.Background()
_, err := retryx.Do(ctx, func(rc retryx.RetryController) (string, error) {
rc.Abort()
return "", errors.New("fatal")
}, retryx.WithMaxAttempts(5))
fmt.Println(err != nil)
}
Output: true
Types ¶
type Option ¶
type Option func(*config)
Option configures Do behavior.
func WithBackoff ¶
WithBackoff sets the base backoff duration for exponential backoff. The actual delay for attempt i is: min(base * 2^i, maxBackoff) * jitter.
func WithJitter ¶
WithJitter enables or disables random jitter on backoff. Jitter multiplies the delay by a random factor in [0.5, 1.5).
func WithMaxAttempts ¶
WithMaxAttempts sets the maximum number of attempts (including the first). Values <= 0 are treated as 1 by Do (execute once, no retry).
func WithMaxBackoff ¶
WithMaxBackoff sets the upper bound for backoff duration.
func WithOnRetry ¶
WithOnRetry sets a callback invoked after each failed attempt (before the backoff sleep). Useful for logging or metrics. The attempt number is 1-based.
func WithRetryIf ¶
WithRetryIf sets a custom predicate that decides whether an error is retryable. When set, this overrides the default errx.Error.Retryable check. Return true to retry, false to stop.
type RetryController ¶
type RetryController interface {
// Number returns the 1-based attempt number.
Number() int
// Abort signals that the retry loop should stop after this attempt,
// regardless of the error's retryability. Safe to call multiple times.
Abort()
}
RetryController provides per-attempt context and control to the retried function. The implementation is private; callers interact only through this interface.