retry

package module
v0.0.0-...-12a078e Latest Latest
Warning

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

Go to latest
Published: Apr 7, 2026 License: MIT Imports: 6 Imported by: 0

README

try-again-go

A simple and flexible retry library for Go operations.

Features

  • 🔄 Configurable number of retry attempts
  • 🧩 Type-safe Generics: Works with any type T effortlessly. No interface casting, no reflection
  • ⏱️ Flexible delay strategies (fixed, exponential backoff with jitter)
  • 🎯 Smart retryable error detection
  • 🚫 Context cancellation support
  • 📝 Customizable logging
  • 📊 Observability: Lifecycle hooks for metrics and monitoring
  • 🏗️ Simple and clean API

Installation

go get github.com/1amDudman/try-again-go

Quick Start

package main

import (
    "context"
    "fmt"
    "net/http"
    "time"

    retry "github.com/1amDudman/try-again-go"
)

func main() {
    // Create retry config with default settings
    retryConfig := retry.NewRetry()

    // Function to retry
    retryFunc := func() (string, error) {
        resp, err := http.Get("https://example.com")
        if err != nil {
            return "", err
        }

        return "success", nil
    }

    // Execute with retries
    ctx := context.Background()
    result, err := retry.Do(ctx, retryConfig, retryFunc)
    if err != nil {
        fmt.Printf("All attempts failed: %v\n", err)
        return
    }

    fmt.Println("Success!")
}

Configuration

Important: The library uses Go Generics. Your retry function can return any type T using the signature func() (T, error).

Basic Parameters
retryConfig := retry.NewRetry(
    retry.WithAttempts(5),                                    // 5 attempts
    retry.WithDelay(200*time.Millisecond),                   // base delay 200ms
    retry.WithMaxDelay(5*time.Second),                       // max delay 5s
    retry.WithDelayType(retry.ExpBackoffWithJitter()),       // exponential backoff with jitter
    retry.WithLogger(customLogger),                          // custom logger
    retry.WithOnRetry(metricsHook),                          // metrics collection hook
)
Observability & Metrics

If you need to track retry behavior without parsing text logs (e.g., for Prometheus or Datadog), use the OnRetry hook. It executes after a failed attempt, right before the system "sleeps". This is perfect for monitoring the stability of external services, such as third-party APIs or client data synchronizations.

retryConfig := retry.NewRetry(
    retry.WithOnRetry(func(attempt int, err error, delay time.Duration) {
        // Clean metric collection without log parsing
        metrics.IncRetryCount(err.Error())
        metrics.AddSleepTime(delay.Seconds()) 
    }),
)
Delay Strategies
Fixed Delay
retry.WithDelayType(retry.FixedDelay())
Exponential Backoff with Jitter
retry.WithDelayType(retry.ExpBackoffWithJitter())
Logging
type CustomLogger struct{}

func (cl CustomLogger) Printf(format string, v ...any) {
    log.Printf("[RETRY] "+format, v...)
}

retryConfig := retry.NewRetry(
    retry.WithLogger(CustomLogger{}),
)

Error Handling

Non-Retryable Errors

Some errors should not be retried. Use NonRetryable:

func riskyOperation() (string, error) {
    if someCondition {
        return "", retry.NonRetryable(errors.New("critical error"))
    }
    // ...
}
Automatic Detection

The library automatically considers retryable:

  • Network timeouts
  • All errors except those marked as NonRetryable

Operation Cancellation

Use context to cancel operations:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

result, err := retry.Do(ctx, retryConfig, retryFunc)

Default Settings

  • Attempts: 3
  • Base delay: 100ms
  • Max delay: 1s
  • Delay strategy: Fixed
  • Logger: No output
  • OnRetry: No-op (silent)

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Do

func Do[T any](ctx context.Context, rc *RetryConfig, fn RetryFunc[T]) (T, error)

Do executes the retry logic with the provided context and retry function. It attempts the operation up to the configured number of times, with delays between attempts calculated by the configured delay strategy.

The method handles:

  • Context cancellation (respects ctx.Done())
  • Non-retryable errors (marked with NonRetryable())
  • Delay calculation and sleeping between attempts
  • Comprehensive logging of retry events

Returns the successful result or the last error encountered after all attempts have been exhausted.

Example:

ctx := context.WithTimeout(context.Background(), 30*time.Second)
result, err := Do(ctx, config, retryFunc)
if err != nil {
    log.Fatal("All retry attempts failed:", err)
}

func NonRetryable

func NonRetryable(err error) error

NonRetryable wraps an error to explicitly mark it as non-retryable. Use this function to prevent retry attempts for critical errors like authentication failures, invalid input, or configuration errors.

Example:

if unauthorized {
    return nil, retry.NonRetryable(errors.New("invalid credentials"))
}

Types

type DelayTypeFunc

type DelayTypeFunc func(attempt int, baseDelay, maxDelay time.Duration) time.Duration

DelayTypeFunc defines a function type for calculating retry delays. Implementations receive the current attempt number (0-based), base delay, and maximum delay, then return the actual delay to use for that attempt.

Example implementations:

  • Fixed delay: always return baseDelay
  • Linear backoff: return baseDelay * attempt
  • Exponential backoff: return baseDelay * 2^attempt

func ExpBackoffWithJitter

func ExpBackoffWithJitter() DelayTypeFunc

ExpBackoffWithJitter returns a DelayTypeFunc that implements exponential backoff with random jitter. Each retry attempt doubles the delay from the previous attempt, with random jitter added to prevent thundering herd problems.

The algorithm works as follows:

  • Calculate exponential backoff: baseDelay * 2^(attempt-1)
  • Add random jitter: 0 to 20% of the exponential delay
  • Cap the result at maxDelay to prevent infinite growth

This strategy is recommended for most retry scenarios as it provides good balance between quick recovery and system protection.

Example delays with baseDelay=100ms:

  • attempt 1: ~100-120ms
  • attempt 2: ~200-240ms
  • attempt 3: ~400-480ms
  • attempt 4: limited by maxDelay

func FixedDelay

func FixedDelay() DelayTypeFunc

FixedDelay returns a DelayTypeFunc that uses a constant delay between retry attempts. The delay remains the same regardless of attempt number, providing predictable and consistent retry timing.

This strategy is useful when you want simple, uniform delays without the complexity of exponential backoff.

type Logger

type Logger interface {
	Printf(format string, v ...any)
}

Logger interface defines the logging behavior for retry operations. Implementations should provide formatted logging output similar to fmt.Printf. This interface allows users to integrate their preferred logging solution (logrus, zap, standard log, etc.) with the retry library.

Example usage:

type CustomLogger struct{}
func (cl CustomLogger) Printf(format string, v ...any) {
    log.Printf("[RETRY] "+format, v...)
}

retryConfig := retry.NewRetry(retry.WithLogger(CustomLogger{}))

type OnRetryFunc

type OnRetryFunc func(attempt int, err error, delay time.Duration)

OnRetryFunc defines a signature for a lifecycle hook executed after a failed attempt, right before the delay.

type Option

type Option func(*RetryConfig)

Option defines a function type for configuring RetryConfig using the functional options pattern. This allows for flexible and extensible configuration of retry behavior without breaking API compatibility.

func WithAttempts

func WithAttempts(attempts int) Option

WithAttempts sets the number of retry attempts for the RetryConfig. The attempts value determines how many times the operation will be retried before giving up. Must be a positive integer.

Example:

retry.NewRetry(retry.WithAttempts(5))

func WithDelay

func WithDelay(delay time.Duration) Option

WithDelay sets the base delay duration between retry attempts. This delay is used as the foundation for delay calculations in both fixed and exponential backoff strategies.

Example:

retry.NewRetry(retry.WithDelay(200*time.Millisecond))

func WithDelayType

func WithDelayType(delayType DelayTypeFunc) Option

WithDelayType sets the delay calculation function for retry attempts. This allows customization of the delay strategy (fixed, exponential, etc.). The function receives the attempt number, base delay, and max delay.

Example:

retry.NewRetry(retry.WithDelayType(retry.ExpBackoffWithJitter()))

func WithLogger

func WithLogger(logger Logger) Option

WithLogger sets a custom logger for retry operations. The logger will receive detailed information about retry attempts, failures, and timing. Use this to integrate retry logging with your application's logging system.

Example:

logger := log.New(os.Stdout, "[RETRY] ", log.LstdFlags)
retry.NewRetry(retry.WithLogger(logger))

func WithMaxDelay

func WithMaxDelay(maxDelay time.Duration) Option

WithMaxDelay sets the maximum delay duration that can be used between retry attempts. This prevents exponential backoff from growing indefinitely and ensures reasonable upper bounds on retry delays.

Example:

retry.NewRetry(retry.WithMaxDelay(30*time.Second))

func WithOnRetry

func WithOnRetry(fn OnRetryFunc) Option

WithOnRetry sets a hook for retry operations especially for metrics. The onRetry will receive detailed information about retry attempts, failures, and timing. Use this to integrate retry hook with your application's metrics system.

Example:

retry.NewRetry(

retry.WithOnRetry(func(attempt int, err error, delay time.Duration) {
    metrics.IncRetryCount(err.Error())
    metrics.AddSleepTime(delay.Seconds())
}),

)

type RetryConfig

type RetryConfig struct {
	// contains filtered or unexported fields
}

RetryConfig holds the complete configuration for retry behavior. It encapsulates all retry parameters including attempts, delays, logging, and delay calculation strategy. Use NewRetry() to create instances with sensible defaults and functional options for customization.

func NewRetry

func NewRetry(opts ...Option) *RetryConfig

NewRetry creates a new RetryConfig with sensible default values and applies the provided functional options. The defaults are designed for common use cases but can be easily customized using the With* option functions.

Default configuration:

  • 3 retry attempts
  • 100ms base delay
  • 1s maximum delay
  • Fixed delay strategy
  • Silent logging (nopLogger)

Example:

config := retry.NewRetry(
    retry.WithAttempts(5),
    retry.WithDelay(200*time.Millisecond),
    retry.WithDelayType(retry.ExpBackoffWithJitter()),
)

type RetryFunc

type RetryFunc[T any] func() (T, error)
    return "success", nil
}

Jump to

Keyboard shortcuts

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