resilience

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2026 License: MIT Imports: 9 Imported by: 4

Documentation

Overview

Package resilience provides error handling, retry logic, and backoff strategies for building resilient voice applications.

The package is designed to work with the AX (Agent Experience) specification, enabling intelligent error recovery based on error categorization.

Error Categories

Errors are classified into categories that determine handling strategy:

  • CategoryTransient: Temporary failures, retry with backoff
  • CategoryRateLimit: Rate limited, retry with longer backoff
  • CategoryValidation: Invalid input, don't retry
  • CategoryAuth: Authentication failure, don't retry
  • CategoryNotFound: Resource not found, don't retry
  • CategoryServer: Server error, retry with backoff
  • CategoryQuota: Quota exceeded, don't retry
  • CategoryUnknown: Unknown error, use default behavior

Retry Logic

The Retry and RetryWithResult functions execute operations with automatic retries for retryable errors:

result, err := resilience.RetryWithResult(ctx, resilience.DefaultRetryConfig(), func() (string, error) {
    return api.Call()
})

Backoff Strategies

ExponentialBackoff provides exponential backoff with jitter:

backoff := &resilience.ExponentialBackoff{
    Initial:    time.Second,
    Max:        30 * time.Second,
    Multiplier: 2.0,
    Jitter:     0.1,
}

Error Wrapping

ProviderError wraps provider errors with AX metadata:

err := &resilience.ProviderError{
    Provider: "elevenlabs",
    Op:       "Synthesize",
    Err:      originalErr,
    Info: resilience.ErrorInfo{
        Category:   resilience.CategoryRateLimit,
        Retryable:  true,
        Code:       "RATE_LIMITED",
        Suggestion: "Wait and retry with exponential backoff",
    },
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Do

func Do(ctx context.Context, fn func() error) error

Do is a convenience wrapper for Retry with default config.

func DoWithResult

func DoWithResult[T any](ctx context.Context, fn func() (T, error)) (T, error)

DoWithResult is a convenience wrapper for RetryWithResult with default config.

func GetRetryAfter

func GetRetryAfter(err error) time.Duration

GetRetryAfter extracts retry-after duration from an error. Returns zero if not a ProviderError or no retry-after specified.

func IsRetryable

func IsRetryable(err error) bool

IsRetryable checks if an error should be retried.

func Retry

func Retry(ctx context.Context, config RetryConfig, fn func() error) error

Retry executes fn with retries according to config.

It returns nil on success, or the last error after all retries are exhausted. If the context is canceled, it returns the context error immediately.

Example:

err := resilience.Retry(ctx, resilience.DefaultRetryConfig(), func() error {
    return api.Call()
})

func RetryWithResult

func RetryWithResult[T any](ctx context.Context, config RetryConfig, fn func() (T, error)) (T, error)

RetryWithResult executes fn with retries and returns the result on success.

Example:

result, err := resilience.RetryWithResult(ctx, config, func() (string, error) {
    return api.GetValue()
})

Types

type BackoffStrategy

type BackoffStrategy interface {
	// NextDelay returns the delay before the next retry attempt.
	// attempt is 1-indexed (first retry is attempt 1).
	NextDelay(attempt int) time.Duration

	// Reset resets the backoff state for a new operation.
	Reset()
}

BackoffStrategy computes delay between retries.

func DefaultBackoff

func DefaultBackoff() BackoffStrategy

DefaultBackoff returns a sensible default exponential backoff strategy.

type ConstantBackoff

type ConstantBackoff struct {
	// Delay is the constant delay between retries.
	Delay time.Duration
}

ConstantBackoff always returns the same delay.

func (*ConstantBackoff) NextDelay

func (b *ConstantBackoff) NextDelay(attempt int) time.Duration

NextDelay returns the constant delay.

func (*ConstantBackoff) Reset

func (b *ConstantBackoff) Reset()

Reset resets the backoff state (no-op for stateless implementation).

type DefaultClassifier

type DefaultClassifier struct{}

DefaultClassifier provides basic error classification based on common patterns. Use this as a fallback when no provider-specific classifier is available.

func (*DefaultClassifier) Classify

func (c *DefaultClassifier) Classify(err error) ErrorInfo

Classify categorizes an error using common heuristics.

type ErrorCategory

type ErrorCategory string

ErrorCategory classifies errors for handling decisions.

const (
	// CategoryTransient indicates a temporary failure that may succeed on retry.
	// Examples: network timeout, connection reset.
	CategoryTransient ErrorCategory = "transient"

	// CategoryRateLimit indicates the request was rate limited.
	// Retry with longer backoff, respect Retry-After header if present.
	CategoryRateLimit ErrorCategory = "rate_limit"

	// CategoryValidation indicates invalid input that will never succeed.
	// Do not retry; fix the request first.
	CategoryValidation ErrorCategory = "validation"

	// CategoryAuth indicates an authentication or authorization failure.
	// Do not retry; re-authenticate or check permissions.
	CategoryAuth ErrorCategory = "auth"

	// CategoryNotFound indicates the requested resource does not exist.
	// Do not retry; the resource must be created first.
	CategoryNotFound ErrorCategory = "not_found"

	// CategoryServer indicates an internal server error.
	// May succeed on retry; use exponential backoff.
	CategoryServer ErrorCategory = "server"

	// CategoryQuota indicates a quota or limit has been exceeded.
	// Do not retry; quota must be increased or reset.
	CategoryQuota ErrorCategory = "quota"

	// CategoryUnknown indicates the error could not be classified.
	// Use default behavior (typically no retry).
	CategoryUnknown ErrorCategory = "unknown"
)

func (ErrorCategory) IsRetryable

func (c ErrorCategory) IsRetryable() bool

IsRetryable returns true if errors in this category should typically be retried.

func (ErrorCategory) String

func (c ErrorCategory) String() string

String returns the string representation of the category.

func (ErrorCategory) SuggestedAction

func (c ErrorCategory) SuggestedAction() string

SuggestedAction returns a human-readable suggestion for handling this category.

type ErrorClassifier

type ErrorClassifier interface {
	// Classify analyzes an error and returns its metadata.
	Classify(err error) ErrorInfo
}

ErrorClassifier categorizes errors from any source. Implementations should be provider-specific.

type ErrorInfo

type ErrorInfo struct {
	// Category classifies the error for handling decisions.
	Category ErrorCategory

	// Retryable indicates if the operation can be safely retried.
	Retryable bool

	// Code is a machine-readable error code (e.g., "RATE_LIMITED").
	Code string

	// Message is a human-readable error description.
	Message string

	// Suggestion provides guidance for error recovery.
	Suggestion string

	// RetryAfter hints how long to wait before retrying.
	// Zero means use default backoff strategy.
	RetryAfter time.Duration
}

ErrorInfo provides actionable metadata about an error.

func GetErrorInfo

func GetErrorInfo(err error) ErrorInfo

GetErrorInfo extracts ErrorInfo from an error. If the error is not a ProviderError, returns an unknown category.

type ExponentialBackoff

type ExponentialBackoff struct {
	// Initial is the delay for the first retry (default: 1s).
	Initial time.Duration

	// Max is the maximum delay (default: 30s).
	Max time.Duration

	// Multiplier is the factor by which delay increases (default: 2.0).
	Multiplier float64

	// Jitter is the random factor applied to delays (0-1, default: 0.1).
	// A jitter of 0.1 means ±10% randomization.
	Jitter float64
}

ExponentialBackoff implements exponential backoff with optional jitter.

The delay for attempt n is: min(Initial * Multiplier^(n-1) * (1 ± Jitter), Max)

Example with Initial=1s, Multiplier=2, Max=30s:

  • Attempt 1: 1s
  • Attempt 2: 2s
  • Attempt 3: 4s
  • Attempt 4: 8s
  • Attempt 5: 16s
  • Attempt 6+: 30s (capped)

func (*ExponentialBackoff) NextDelay

func (b *ExponentialBackoff) NextDelay(attempt int) time.Duration

NextDelay returns the delay before the next retry attempt.

func (*ExponentialBackoff) Reset

func (b *ExponentialBackoff) Reset()

Reset resets the backoff state (no-op for stateless implementation).

type HTTPStatusClassifier

type HTTPStatusClassifier struct{}

HTTPStatusClassifier classifies errors based on HTTP status codes.

func (*HTTPStatusClassifier) ClassifyStatus

func (c *HTTPStatusClassifier) ClassifyStatus(status int, message string) ErrorInfo

ClassifyStatus returns ErrorInfo based on HTTP status code.

type LinearBackoff

type LinearBackoff struct {
	// Initial is the delay for the first retry (default: 1s).
	Initial time.Duration

	// Increment is added for each subsequent attempt (default: 1s).
	Increment time.Duration

	// Max is the maximum delay (default: 30s).
	Max time.Duration
}

LinearBackoff increases delay linearly with each attempt.

The delay for attempt n is: min(Initial + Increment*(n-1), Max)

func (*LinearBackoff) NextDelay

func (b *LinearBackoff) NextDelay(attempt int) time.Duration

NextDelay returns the delay before the next retry attempt.

func (*LinearBackoff) Reset

func (b *LinearBackoff) Reset()

Reset resets the backoff state (no-op for stateless implementation).

type NoBackoff

type NoBackoff struct{}

NoBackoff returns zero delay (immediate retry). Use with caution; typically only for testing.

func (*NoBackoff) NextDelay

func (b *NoBackoff) NextDelay(attempt int) time.Duration

NextDelay returns zero delay.

func (*NoBackoff) Reset

func (b *NoBackoff) Reset()

Reset resets the backoff state (no-op).

type ProviderError

type ProviderError struct {
	// Provider is the name of the provider (e.g., "elevenlabs").
	Provider string

	// Op is the operation that failed (e.g., "Synthesize").
	Op string

	// Err is the underlying error.
	Err error

	// Info contains AX metadata about the error.
	Info ErrorInfo
}

ProviderError wraps provider errors with AX metadata. It implements the error interface and supports error unwrapping.

func IsProviderError

func IsProviderError(err error) (*ProviderError, bool)

IsProviderError checks if err is a ProviderError and returns it.

func NewProviderError

func NewProviderError(provider, op string, err error, info ErrorInfo) *ProviderError

NewProviderError creates a new ProviderError.

func (*ProviderError) Error

func (e *ProviderError) Error() string

Error returns the error message.

func (*ProviderError) GetCategory

func (e *ProviderError) GetCategory() ErrorCategory

GetCategory returns the error category.

func (*ProviderError) GetCode

func (e *ProviderError) GetCode() string

GetCode returns the error code.

func (*ProviderError) GetRetryAfter

func (e *ProviderError) GetRetryAfter() time.Duration

GetRetryAfter returns the suggested retry delay.

func (*ProviderError) GetSuggestion

func (e *ProviderError) GetSuggestion() string

GetSuggestion returns the recovery suggestion.

func (*ProviderError) Is

func (e *ProviderError) Is(target error) bool

Is reports whether any error in the chain matches target.

func (*ProviderError) IsRetryable

func (e *ProviderError) IsRetryable() bool

IsRetryable returns true if the error can be retried.

func (*ProviderError) Unwrap

func (e *ProviderError) Unwrap() error

Unwrap returns the underlying error.

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the maximum number of attempts (including initial).
	// Default: 3 (1 initial + 2 retries).
	MaxAttempts int

	// Backoff is the strategy for computing delays between retries.
	// Default: ExponentialBackoff with sensible defaults.
	Backoff BackoffStrategy

	// Classifier categorizes errors to determine retryability.
	// Default: DefaultClassifier.
	Classifier ErrorClassifier

	// RetryIf is an optional predicate for custom retry decisions.
	// If set, overrides the classifier's retryability decision.
	// Return true to retry, false to stop.
	RetryIf func(err error) bool

	// OnRetry is called before each retry attempt.
	// Can be used for logging or metrics.
	OnRetry func(attempt int, err error, delay time.Duration)
}

RetryConfig controls retry behavior.

func DefaultRetryConfig

func DefaultRetryConfig() RetryConfig

DefaultRetryConfig returns a RetryConfig with sensible defaults.

func (*RetryConfig) Validate

func (c *RetryConfig) Validate()

Validate ensures the config has valid values, applying defaults where needed.

type RetryError

type RetryError struct {
	// Attempts is the number of attempts made.
	Attempts int

	// Err is the last error encountered.
	Err error
}

RetryError wraps the last error with retry context.

func (*RetryError) Error

func (e *RetryError) Error() string

Error returns the error message.

func (*RetryError) Unwrap

func (e *RetryError) Unwrap() error

Unwrap returns the underlying error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL