Documentation
¶
Overview ¶
Package retry provides retry policies with configurable backoff strategies for resilient operations.
This package offers a comprehensive retry mechanism with multiple backoff strategies, error classification, and flexible policy configuration. It is designed for handling transient failures in network operations, API calls, and other unreliable operations.
Basic Usage ¶
Use the default executor for simple retry scenarios:
executor := retry.NewDefaultRetryExecutor()
err := executor.Execute(ctx, func() error {
return apiCall()
})
Custom Policies ¶
Create custom retry policies for specific requirements:
policy := retry.DefaultRetryPolicy().
WithMaxRetries(5).
WithInitialDelay(100 * time.Millisecond)
strategy := retry.NewExponentialBackoffStrategy(policy)
executor := retry.NewRetryExecutor(policy, strategy)
Backoff Strategies ¶
Multiple backoff strategies are available:
- ExponentialBackoffStrategy: Exponential delay increase with jitter
- ConstantBackoffStrategy: Fixed delay between retries
- LinearBackoffStrategy: Linear delay increase
Error Classification ¶
Errors can be marked as retryable or non-retryable:
err := retry.MarkRetryable(errors.New("temporary failure"), 503)
if retry.IsRetryableError(err) {
// Handle retryable error
}
Example (BackoffStrategies) ¶
Example_backoffStrategies demonstrates different backoff strategies
package main
import (
"fmt"
"time"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
policy := retry.DefaultRetryPolicy().
WithJitter(0.0) // No jitter for predictable output
// Exponential backoff (without jitter for predictable output)
expStrategy := retry.NewExponentialBackoffStrategy(policy).
WithJitterType(retry.NoJitter)
fmt.Printf("Exponential delay (attempt 0): %v\n", expStrategy.NextDelay(0, nil))
fmt.Printf("Exponential delay (attempt 1): %v\n", expStrategy.NextDelay(1, nil))
// Constant backoff
constStrategy := retry.NewConstantBackoffStrategy(2 * time.Second)
fmt.Printf("Constant delay (attempt 0): %v\n", constStrategy.NextDelay(0, nil))
fmt.Printf("Constant delay (attempt 1): %v\n", constStrategy.NextDelay(1, nil))
// Linear backoff
linearStrategy := retry.NewLinearBackoffStrategy(1*time.Second, 1*time.Second, 10*time.Second)
fmt.Printf("Linear delay (attempt 0): %v\n", linearStrategy.NextDelay(0, nil))
fmt.Printf("Linear delay (attempt 1): %v\n", linearStrategy.NextDelay(1, nil))
}
Output: Exponential delay (attempt 0): 1s Exponential delay (attempt 1): 2s Constant delay (attempt 0): 2s Constant delay (attempt 1): 2s Linear delay (attempt 0): 1s Linear delay (attempt 1): 2s
Example (Basic) ¶
Example_basic demonstrates basic retry usage
package main
import (
"context"
"errors"
"fmt"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
ctx := context.Background()
executor := retry.NewDefaultRetryExecutor()
// Simulate an operation that fails twice then succeeds
attempt := 0
err := executor.Execute(ctx, func() error {
attempt++
if attempt < 3 {
return retry.MarkRetryable(errors.New("temporary error"), 503)
}
return nil
})
if err != nil {
fmt.Printf("Failed: %v\n", err)
} else {
fmt.Printf("Succeeded after %d attempts\n", attempt)
}
}
Output: Succeeded after 3 attempts
Example (Callback) ¶
Example_callback demonstrates using retry callbacks for monitoring
package main
import (
"context"
"errors"
"fmt"
"time"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
ctx := context.Background()
executor := retry.NewDefaultRetryExecutor()
attempt := 0
err := executor.ExecuteWithCallback(
ctx,
func() error {
attempt++
if attempt < 3 {
return retry.MarkRetryable(errors.New("temporary error"), 503)
}
return nil
},
func(attemptNum int, err error, delay time.Duration) {
// This callback is called before each retry
// In a real application, you might log this or send metrics
_ = attemptNum
_ = err
_ = delay
},
)
if err != nil {
fmt.Printf("Failed: %v\n", err)
} else {
fmt.Println("Success")
}
}
Output: Success
Example (CustomPolicy) ¶
Example_customPolicy demonstrates using a custom retry policy
package main
import (
"context"
"fmt"
"time"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
ctx := context.Background()
// Create a custom policy with aggressive retries
policy := retry.DefaultRetryPolicy().
WithMaxRetries(5).
WithInitialDelay(100 * time.Millisecond)
strategy := retry.NewExponentialBackoffStrategy(policy).
WithJitterType(retry.FullJitter)
executor := retry.NewRetryExecutor(policy, strategy)
// Execute with custom policy
err := executor.Execute(ctx, func() error {
// Your operation here
return nil
})
if err != nil {
fmt.Printf("Failed: %v\n", err)
} else {
fmt.Println("Success")
}
}
Output: Success
Example (ErrorClassification) ¶
Example_errorClassification demonstrates error classification
package main
import (
"errors"
"fmt"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
// Check if status codes are retryable
fmt.Printf("429 retryable: %v\n", retry.IsRetryableStatusCode(429))
fmt.Printf("500 retryable: %v\n", retry.IsRetryableStatusCode(500))
fmt.Printf("400 retryable: %v\n", retry.IsRetryableStatusCode(400))
// Create and check retryable errors
err := retry.MarkRetryable(errors.New("temporary failure"), 503)
fmt.Printf("Error retryable: %v\n", retry.IsRetryableError(err))
}
Output: 429 retryable: true 500 retryable: true 400 retryable: false Error retryable: true
Example (ExecutorWithCustomPolicy) ¶
Example_executorWithCustomPolicy demonstrates modifying executor behavior
package main
import (
"context"
"fmt"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
ctx := context.Background()
executor := retry.NewDefaultRetryExecutor()
// Use a conservative policy for this specific operation
conservativeExecutor := executor.WithPolicy(retry.ConservativeRetryPolicy())
err := conservativeExecutor.Execute(ctx, func() error {
// Operation with conservative retry behavior
return nil
})
if err != nil {
fmt.Printf("Failed: %v\n", err)
} else {
fmt.Println("Success with conservative retry")
}
}
Output: Success with conservative retry
Example (ExecutorWithCustomStrategy) ¶
Example_executorWithCustomStrategy demonstrates modifying backoff strategy
package main
import (
"context"
"fmt"
"time"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
ctx := context.Background()
executor := retry.NewDefaultRetryExecutor()
// Use constant backoff instead of exponential
constantExecutor := executor.WithStrategy(
retry.NewConstantBackoffStrategy(1 * time.Second),
)
err := constantExecutor.Execute(ctx, func() error {
// Operation with constant backoff
return nil
})
if err != nil {
fmt.Printf("Failed: %v\n", err)
} else {
fmt.Println("Success with constant backoff")
}
}
Output: Success with constant backoff
Example (PresetPolicies) ¶
Example_presetPolicies demonstrates using preset retry policies
package main
import (
"fmt"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
// Default policy (balanced retries)
defaultPolicy := retry.DefaultRetryPolicy()
fmt.Printf("Default MaxRetries: %d\n", defaultPolicy.MaxRetries)
fmt.Printf("Default InitialDelay: %v\n", defaultPolicy.InitialDelay)
// No retry policy (disable retries)
noRetryPolicy := retry.NoRetryPolicy()
fmt.Printf("NoRetry MaxRetries: %d\n", noRetryPolicy.MaxRetries)
// Aggressive policy (more retries, shorter delays)
aggressivePolicy := retry.AggressiveRetryPolicy()
fmt.Printf("Aggressive MaxRetries: %d\n", aggressivePolicy.MaxRetries)
fmt.Printf("Aggressive InitialDelay: %v\n", aggressivePolicy.InitialDelay)
// Conservative policy (fewer retries, longer delays)
conservativePolicy := retry.ConservativeRetryPolicy()
fmt.Printf("Conservative MaxRetries: %d\n", conservativePolicy.MaxRetries)
fmt.Printf("Conservative InitialDelay: %v\n", conservativePolicy.InitialDelay)
}
Output: Default MaxRetries: 3 Default InitialDelay: 1s NoRetry MaxRetries: 0 Aggressive MaxRetries: 5 Aggressive InitialDelay: 500ms Conservative MaxRetries: 2 Conservative InitialDelay: 2s
Example (Typed) ¶
Example_typed demonstrates using typed retry operations
package main
import (
"context"
"errors"
"fmt"
"github.com/cecil-the-coder/ai-provider-kit/pkg/retry"
)
func main() {
ctx := context.Background()
executor := retry.NewDefaultRetryExecutor()
// Execute a typed operation
attempt := 0
result, err := retry.ExecuteTyped(ctx, executor, func() (string, error) {
attempt++
if attempt < 2 {
return "", retry.MarkRetryable(errors.New("temporary error"), 503)
}
return "success", nil
})
if err != nil {
fmt.Printf("Failed: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
}
Output: Result: success
Index ¶
- Constants
- func ExecuteTyped[T any](ctx context.Context, executor *RetryExecutor, operation ExecuteFunc[T]) (T, error)
- func GetStatusCode(err error) int
- func IsRetryableError(err error) bool
- func IsRetryableStatusCode(statusCode int) bool
- func MarkNonRetryable(err error, statusCode int) error
- func MarkRetryable(err error, statusCode int) error
- func NewRetryableError(err error, retryable bool, statusCode int) error
- type BackoffStrategy
- type ConstantBackoffStrategy
- type ExecuteFunc
- type ExponentialBackoffStrategy
- type JitterType
- type LinearBackoffStrategy
- type OnRetryFunc
- type Policy
- type RetryExecutor
- func (r *RetryExecutor) Execute(ctx context.Context, operation func() error) error
- func (r *RetryExecutor) ExecuteWithCallback(ctx context.Context, operation func() error, onRetry OnRetryFunc) error
- func (r *RetryExecutor) ExecuteWithResult(ctx context.Context, operation func() (interface{}, error)) (interface{}, error)
- func (r *RetryExecutor) GetPolicy() *Policy
- func (r *RetryExecutor) GetStrategy() BackoffStrategy
- func (r *RetryExecutor) WithPolicy(policy *Policy) *RetryExecutor
- func (r *RetryExecutor) WithStrategy(strategy BackoffStrategy) *RetryExecutor
- type RetryStrategy
- type RetryableError
Examples ¶
Constants ¶
const ( // NoJitter applies no randomization to the delay. NoJitter = retry.NoJitter // FullJitter randomizes the delay between 0 and the calculated delay. FullJitter = retry.FullJitter // EqualJitter splits the delay evenly between fixed and random components. EqualJitter = retry.EqualJitter DecorrelatedJitter = retry.DecorrelatedJitter )
Jitter type constants.
const ( StatusTooManyRequests = retry.StatusTooManyRequests // 429 StatusInternalServerError = retry.StatusInternalServerError // 500 StatusBadGateway = retry.StatusBadGateway // 502 StatusGatewayTimeout = retry.StatusGatewayTimeout // 504 StatusInsufficientStorage = retry.StatusInsufficientStorage // 507 StatusNetworkAuthRequired = retry.StatusNetworkAuthRequired // 511 )
Common HTTP status codes that may trigger retries.
Variables ¶
This section is empty.
Functions ¶
func ExecuteTyped ¶
func ExecuteTyped[T any](ctx context.Context, executor *RetryExecutor, operation ExecuteFunc[T]) (T, error)
ExecuteTyped executes a typed function with retry logic. It wraps the generic ExecuteWithResult to provide type-safe returns.
Example:
result, err := retry.ExecuteTyped(ctx, executor, func() (string, error) {
return api.Call()
})
func GetStatusCode ¶
GetStatusCode extracts the HTTP status code from an error if available. Returns 0 if no status code is associated with the error.
func IsRetryableError ¶
IsRetryableError checks if an error is retryable. It examines the error chain for RetryableError implementations.
Example:
if retry.IsRetryableError(err) {
// Handle retryable error
}
func IsRetryableStatusCode ¶
IsRetryableStatusCode checks if an HTTP status code is retryable. Returns true for common transient status codes (429, 500, 502, 503, 504, 507, 511).
Example:
if retry.IsRetryableStatusCode(resp.StatusCode) {
// Trigger retry
}
func MarkNonRetryable ¶
MarkNonRetryable wraps an error to mark it as non-retryable with the given status code. This is a convenience function for NewRetryableError with retryable=false.
Example:
return retry.MarkNonRetryable(authErr, 401)
func MarkRetryable ¶
MarkRetryable wraps an error to mark it as retryable with the given status code. This is a convenience function for NewRetryableError with retryable=true.
Example:
return retry.MarkRetryable(apiErr, 503)
func NewRetryableError ¶
NewRetryableError creates a new retryable error with the specified status code. The retryable parameter determines if the error should trigger retries.
Example:
err := retry.NewRetryableError(errors.New("API error"), true, 503)
Types ¶
type BackoffStrategy ¶
type BackoffStrategy = retry.BackoffStrategy
BackoffStrategy defines the interface for calculating retry delays. Implementations provide different algorithms for determining how long to wait between retry attempts.
type ConstantBackoffStrategy ¶
type ConstantBackoffStrategy = retry.ConstantBackoffStrategy
ConstantBackoffStrategy implements a constant delay between retries. The same delay is used for all retry attempts.
func NewConstantBackoffStrategy ¶
func NewConstantBackoffStrategy(delay time.Duration) *ConstantBackoffStrategy
NewConstantBackoffStrategy creates a new constant backoff strategy. The same delay is used for all retry attempts.
Example:
strategy := retry.NewConstantBackoffStrategy(2 * time.Second)
type ExecuteFunc ¶
ExecuteFunc is a generic function type that works with any return type. This is useful for strongly-typed operations.
type ExponentialBackoffStrategy ¶
type ExponentialBackoffStrategy = retry.ExponentialBackoffStrategy
ExponentialBackoffStrategy implements exponential backoff with configurable jitter. The delay increases exponentially with each retry attempt, optionally randomized with jitter.
func NewExponentialBackoffStrategy ¶
func NewExponentialBackoffStrategy(policy *Policy) *ExponentialBackoffStrategy
NewExponentialBackoffStrategy creates a new exponential backoff strategy. Uses EqualJitter by default, which can be changed with WithJitterType.
Example:
policy := retry.DefaultRetryPolicy()
strategy := retry.NewExponentialBackoffStrategy(policy).
WithJitterType(retry.FullJitter)
type JitterType ¶
type JitterType = retry.JitterType
JitterType defines different types of jitter strategies for randomizing delays. Jitter helps prevent thundering herd problems when multiple clients retry simultaneously.
type LinearBackoffStrategy ¶
type LinearBackoffStrategy = retry.LinearBackoffStrategy
LinearBackoffStrategy implements a linear increase in delay. The delay increases by a fixed increment with each retry attempt.
func NewLinearBackoffStrategy ¶
func NewLinearBackoffStrategy(initialDelay, increment, maxDelay time.Duration) *LinearBackoffStrategy
NewLinearBackoffStrategy creates a new linear backoff strategy. The delay increases by a fixed increment with each retry attempt, capped at maxDelay.
Example:
strategy := retry.NewLinearBackoffStrategy(
1*time.Second, // initialDelay
500*time.Millisecond, // increment
30*time.Second, // maxDelay
)
type OnRetryFunc ¶
type OnRetryFunc = retry.OnRetryFunc
OnRetryFunc is a callback function type that is called before each retry attempt. It receives the current attempt number, the error that triggered the retry, and the delay before the next attempt.
type Policy ¶
type Policy = retry.RetryPolicy
Policy defines the configuration for retry behavior. It specifies how many times to retry, delays between attempts, and which errors/status codes should trigger retries.
func AggressiveRetryPolicy ¶
func AggressiveRetryPolicy() *Policy
AggressiveRetryPolicy returns a policy with more retry attempts and shorter delays. Useful for testing or services with very high availability:
- MaxRetries: 5
- InitialDelay: 500ms
- MaxDelay: 10 seconds
- Multiplier: 1.5
- Jitter: 0.2 (20%)
func ConservativeRetryPolicy ¶
func ConservativeRetryPolicy() *Policy
ConservativeRetryPolicy returns a policy with fewer retries and longer delays. Useful for rate-limited APIs or to avoid overwhelming services:
- MaxRetries: 2
- InitialDelay: 2 seconds
- MaxDelay: 60 seconds
- Multiplier: 3.0
- Jitter: 0.05 (5%)
func DefaultRetryPolicy ¶
func DefaultRetryPolicy() *Policy
DefaultRetryPolicy returns a retry policy with sensible defaults:
- MaxRetries: 3
- InitialDelay: 1 second
- MaxDelay: 30 seconds
- Multiplier: 2.0
- Jitter: 0.1 (10%)
func NoRetryPolicy ¶
func NoRetryPolicy() *Policy
NoRetryPolicy returns a policy that never retries (MaxRetries: 0). Useful for disabling retry behavior while maintaining a consistent API.
type RetryExecutor ¶
type RetryExecutor struct {
// contains filtered or unexported fields
}
RetryExecutor handles the execution of operations with retry logic. It combines a retry policy with a backoff strategy to provide automatic retry functionality for operations.
func NewDefaultRetryExecutor ¶
func NewDefaultRetryExecutor() *RetryExecutor
NewDefaultRetryExecutor creates a retry executor with default settings. Uses DefaultRetryPolicy() and ExponentialBackoffStrategy with EqualJitter.
func NewRetryExecutor ¶
func NewRetryExecutor(policy *Policy, strategy BackoffStrategy) *RetryExecutor
NewRetryExecutor creates a new retry executor with the given policy and strategy.
Example:
policy := retry.DefaultRetryPolicy() strategy := retry.NewExponentialBackoffStrategy(policy) executor := retry.NewRetryExecutor(policy, strategy)
func (*RetryExecutor) Execute ¶
func (r *RetryExecutor) Execute(ctx context.Context, operation func() error) error
Execute executes a function with retry logic. The function is called repeatedly until it succeeds, a non-retryable error occurs, or the maximum number of retries is reached.
The context can be used to cancel the retry operation.
func (*RetryExecutor) ExecuteWithCallback ¶
func (r *RetryExecutor) ExecuteWithCallback( ctx context.Context, operation func() error, onRetry OnRetryFunc, ) error
ExecuteWithCallback executes a function with retry logic and callback notifications. The onRetry callback is invoked before each retry attempt, allowing for logging, metrics collection, or custom retry handling.
func (*RetryExecutor) ExecuteWithResult ¶
func (r *RetryExecutor) ExecuteWithResult(ctx context.Context, operation func() (interface{}, error)) (interface{}, error)
ExecuteWithResult executes a function that returns a result and error with retry logic. Returns the result from the first successful attempt or the last result if all retries fail.
func (*RetryExecutor) GetPolicy ¶
func (r *RetryExecutor) GetPolicy() *Policy
GetPolicy returns the current retry policy.
func (*RetryExecutor) GetStrategy ¶
func (r *RetryExecutor) GetStrategy() BackoffStrategy
GetStrategy returns the current backoff strategy.
func (*RetryExecutor) WithPolicy ¶
func (r *RetryExecutor) WithPolicy(policy *Policy) *RetryExecutor
WithPolicy creates a new executor with a different policy. Returns a new RetryExecutor instance without modifying the original.
func (*RetryExecutor) WithStrategy ¶
func (r *RetryExecutor) WithStrategy(strategy BackoffStrategy) *RetryExecutor
WithStrategy creates a new executor with a different strategy. Returns a new RetryExecutor instance without modifying the original.
type RetryStrategy ¶
type RetryStrategy = retry.RetryStrategy
RetryStrategy defines the interface for retry execution. It provides the NextDelay method for calculating delays before retry attempts.
type RetryableError ¶
type RetryableError = retry.RetryableError
RetryableError is an interface for errors that can indicate retry possibility. Errors implementing this interface can explicitly declare whether they should be retried.