A flexible and configurable Go package for automatically retrying operations that may fail intermittently.
- Supports various backoff strategies:
- Linear
- Constant
- Exponential with jitter
- Random interval
- Custom backoff strategy
- Context cancellation support
- Custom logging capabilities
- Configurable through functional options
To install the retry
package, run:
go get github.com/simp-lee/retry
package main
import (
"fmt"
"time"
"github.com/simp-lee/retry"
)
func main() {
// Retry the operation up to 5 times with a 2-second linear backoff
err := retry.Do(someFunction,
retry.WithTimes(5),
retry.WithLinearBackoff(2*time.Second))
if err != nil {
if retryErr, ok := err.(*retry.Error); ok {
fmt.Printf("Operation failed after %d attempts. Errors: %v\n", retryErr.MaxRetries, retryErr.Errors)
} else {
fmt.Printf("Operation failed: %v\n", err)
}
} else {
fmt.Println("Operation succeeded")
}
}
func someFunction() error {
// Your operation that might fail
return nil
}
retry.Do(someFunction, retry.WithTimes(5), retry.WithLinearBackoff(2*time.Second))
// Retry intervals: 2s, 4s, 6s, 8s, 10s
retry.Do(someFunction, retry.WithTimes(5), retry.WithConstantBackoff(2*time.Second))
// Retry intervals: 2s, 2s, 2s, 2s, 2s
retry.Do(someFunction, retry.WithTimes(4), retry.WithExponentialBackoff(1*time.Second, 10*time.Second, 500*time.Millisecond))
// Retry intervals: 1s (+jitter), 2s (+jitter), 4s (+jitter), 8s (+jitter)
retry.Do(someFunction, retry.WithTimes(5), retry.WithRandomIntervalBackoff(1*time.Second, 3*time.Second))
// Retry intervals: random values between 1s and 3s
type CustomBackoffStrategy struct {
MaxInterval time.Duration
}
func (c *CustomBackoffStrategy) CalculateInterval(attempt int) time.Duration {
interval := time.Duration(attempt) * time.Second
if interval > c.MaxInterval {
return c.MaxInterval
}
return interval
}
func (c *CustomBackoffStrategy) Name() string {
return "Custom"
}
customBackoff := &CustomBackoff{
MaxInterval: 5 * time.Second,
}
retry.Do(someFunction, retry.WithTimes(5), retry.WithCustomBackoff(customBackoff))
// Retry intervals: 1s, 2s, 3s, 4s, 5s
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := retry.Do(someFunction,
retry.WithTimes(5),
retry.WithLinearBackoff(2*time.Second),
retry.WithContext(ctx))
logFunc := func(format string, args ...interface{}) {
slog.Warn(fmt.Sprintf(format, args...))
}
err := retry.Do(someFunction,
retry.WithTimes(5),
retry.WithConstantBackoff(2*time.Second),
retry.WithLogger(logFunc))
retry.Do(retryFunc RetryFunc, options ...Option) error
retry.WithTimes(maxRetries int) Option
retry.WithLinearBackoff(interval time.Duration) Option
retry.WithConstantBackoff(interval time.Duration) Option
retry.WithExponentialBackoff(initialInterval, maxInterval, maxJitter time.Duration) Option
retry.WithRandomIntervalBackoff(minInterval, maxInterval time.Duration) Option
retry.WithCustomBackoff(backoff Backoff) Option
retry.WithContext(ctx context.Context) Option
retry.WithLogger(logFunc func(format string, args ...interface{})) Option
- Use retries for transient failures, not for business logic errors.
- Choose appropriate retry counts and backoff strategies based on your specific use case.
- Always set a maximum retry time or count to prevent infinite loops.
- Use context for timeouts to ensure your retries don't run indefinitely.
- Be mindful of the impact of retries on the system you're interacting with.
- Use custom logging to monitor and debug retry behavior.
Contributions are welcome! Please open an issue or submit a pull request with your changes. Make sure to include tests for new features or bug fixes.
This project is licensed under the MIT License.