retrier

package
v1.152.0 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package retrier provides a configurable retry engine for executing a task function with backoff, jitter, and per-attempt timeouts.

Problem

Transient failures are common in distributed systems (temporary network issues, short-lived upstream overload, brief lock/contention windows). Retrying can dramatically improve success rates, but ad hoc retry loops often miss important details such as cancellation propagation, bounded attempts, timeout isolation per attempt, and jitter to avoid synchronized retry storms.

This package centralizes those concerns in a reusable retrier implementation.

How It Works

New creates a Retrier with defaults, or with custom Option values. Retrier.Run then executes a TaskFn according to configured retry rules:

  1. Execute the task with a per-attempt timeout context.
  2. Evaluate the result with a retry predicate (RetryIfFn).
  3. Stop when attempts are exhausted or retry is not required.
  4. Otherwise schedule the next attempt after delay + random jitter.
  5. Increase delay by the configured multiplication factor for successive retries.

The run loop always respects parent context cancellation.

Defaults

Key Features

Usage

r, err := retrier.New(
    retrier.WithAttempts(5),
    retrier.WithDelay(200*time.Millisecond),
    retrier.WithDelayFactor(2),
    retrier.WithJitter(25*time.Millisecond),
)
if err != nil {
    return err
}

err = r.Run(ctx, func(ctx context.Context) error {
    return callExternalService(ctx)
})
if err != nil {
    return err
}

This package is ideal for retrying idempotent or safe-to-repeat operations in networked and high-concurrency Go services.

Index

Examples

Constants

View Source
const (
	// DefaultAttempts is the default maximum number of total attempts (the
	// initial call plus retries).
	DefaultAttempts = 4

	// DefaultDelay is the default delay to apply after the first failed attempt.
	DefaultDelay = 1 * time.Second

	// DefaultDelayFactor is the default multiplication factor to get the successive delay value.
	DefaultDelayFactor = 2

	// DefaultJitter is the default maximum random Jitter time between retries.
	DefaultJitter = 1 * time.Millisecond

	// DefaultTimeout is the default timeout applied to each function call via context.
	DefaultTimeout = 1 * time.Second
)
View Source
const (
	JitterAdditive = backoff.JitterAdditive // fixed additive ceiling (default)
	JitterFull     = backoff.JitterFull     // rand[0, delay): best decorrelation
	JitterEqual    = backoff.JitterEqual    // delay/2 + rand[0, delay/2)
)

Jitter strategies, re-exported from backoff so callers need not import it.

Variables

This section is empty.

Functions

func DefaultRetryIf

func DefaultRetryIf(err error) bool

DefaultRetryIf is the default retry predicate: returns true if error is non-nil.

Types

type JitterStrategy

type JitterStrategy = backoff.JitterStrategy

JitterStrategy selects how backoff jitter is applied. See backoff.JitterStrategy.

type OnRetryFn

type OnRetryFn func(attempt uint, delay time.Duration, err error)

OnRetryFn is an optional observability callback invoked before each scheduled retry. It receives the number of the attempt that just failed (1-based), the delay before the next attempt, and the error that triggered the retry. It counts scheduled retries — one may still be preempted by cancellation before it runs — and must not panic; it runs inline in Retrier.Run.

type Option

type Option func(c *Retrier) error

Option is the interface that allows to set the options.

func WithAttempts

func WithAttempts(attempts uint) Option

WithAttempts customizes the maximum number of retry attempts. Returns error if attempts < 1.

func WithDelay

func WithDelay(delay time.Duration) Option

WithDelay customizes the base delay after the first failed attempt. Returns error if delay < 1 nanosecond.

func WithDelayFactor

func WithDelayFactor(delayFactor float64) Option

WithDelayFactor customizes the exponential backoff multiplier (factor > 1 for exponential growth). Returns error if delayFactor < 1.

func WithJitter

func WithJitter(jitter time.Duration) Option

WithJitter customizes the maximum random jitter added to each retry delay to avoid thundering-herd. Returns error if jitter < 1 nanosecond.

func WithJitterStrategy

func WithJitterStrategy(strategy JitterStrategy) Option

WithJitterStrategy selects how backoff jitter is applied (default JitterAdditive). JitterFull and JitterEqual scale jitter with the delay for better decorrelation. Returns error if the strategy is not a defined JitterStrategy.

func WithMaxDelay

func WithMaxDelay(maxDelay time.Duration) Option

WithMaxDelay caps the pre-jitter exponential backoff delay (default: unbounded, subject only to the internal safety cap). Returns error if maxDelay < 1 nanosecond.

func WithOnRetry

func WithOnRetry(onRetry OnRetryFn) Option

WithOnRetry registers an observability callback invoked before each scheduled retry (see OnRetryFn). Returns error if the callback is nil.

func WithRetryIfFn

func WithRetryIfFn(retryIfFn RetryIfFn) Option

WithRetryIfFn customizes the retry condition predicate. Returns error if the function is nil.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout customizes the per-attempt timeout applied via context.WithTimeout(). Returns error if timeout < 1 nanosecond.

type Retrier

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

Retrier applies configurable retry logic to generic task functions.

A configured Retrier is immutable: it holds no per-run state, so a single instance is safe to share and to call concurrently from multiple goroutines.

func New

func New(opts ...Option) (*Retrier, error)

New constructs a Retrier with defaults, applying optional configuration.

func (*Retrier) Run

func (r *Retrier) Run(ctx context.Context, task TaskFn) error

Run executes the task with exponential backoff and jitter, respecting parent context cancellation.

An already-canceled context fails fast without running the task. If ctx is canceled during or just before the first attempt, the task may still run once (with the canceled context); a well-behaved task returns promptly.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/tecnickcom/nurago/pkg/retrier"
)

func main() {
	var count int

	// example function that returns nil only at the third attempt.
	task := func(_ context.Context) error {
		if count == 2 {
			return nil
		}

		count++

		return errors.New("ERROR")
	}

	opts := []retrier.Option{
		retrier.WithRetryIfFn(retrier.DefaultRetryIf),
		retrier.WithAttempts(5),
		retrier.WithDelay(10 * time.Millisecond),
		retrier.WithDelayFactor(1.1),
		retrier.WithJitter(5 * time.Millisecond),
		retrier.WithTimeout(2 * time.Millisecond),
	}

	r, err := retrier.New(opts...)
	if err != nil {
		log.Fatal(err)
	}

	timeout := 1 * time.Second

	ctx, cancel := context.WithTimeout(context.TODO(), timeout)

	err = r.Run(ctx, task)

	cancel()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(count)

}
Output:
2

type RetryIfFn

type RetryIfFn func(err error) bool

RetryIfFn is the signature of the function used to decide when retry. It must not panic; it runs inline in Retrier.Run.

type TaskFn

type TaskFn func(ctx context.Context) error

TaskFn is the type of function to be executed.

Jump to

Keyboard shortcuts

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