retry

package
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package retry provides configurable retry logic with exponential backoff and jitter. It is used throughout the platform wherever a transient failure (network timeout, temporary database unavailability, etc.) can be recovered by simply re-attempting the operation after a short delay.

The central entry point is WithBackoff, which executes an operation up to MaxRetries+1 times (1 initial attempt + MaxRetries retries). Between attempts it sleeps for a duration computed by CalculateDelay:

delay = InitialWait * Multiplier^attempt   (capped at MaxWait)
delay ±= delay * JitterFraction            (floored at InitialWait)

Context cancellation is checked before each sleep, so callers can abort a retry loop promptly during graceful shutdown.

Configuration uses the WithDefaults/validate pattern: pass a partially filled Config (or nil) and zero-value fields are replaced with production defaults.

Index

Examples

Constants

View Source
const (
	// DefaultMaxRetries is the number of retry attempts after the initial call fails. Total attempts = 1 (initial) + DefaultMaxRetries. Exported so callers can reference it when building retry-aware error messages.
	DefaultMaxRetries = 3
)

Variables

This section is empty.

Functions

func CalculateDelay

func CalculateDelay(cfg *Config, attempt int) time.Duration

CalculateDelay computes the backoff sleep duration for the given 0-indexed attempt. The formula is:

base   = InitialWait * Multiplier^attempt
capped = min(base, MaxWait)
jitter = capped * JitterFraction * uniform(-1, 1)
delay  = max(capped + jitter, InitialWait)

The delay is always at least InitialWait, even after negative jitter is applied. When JitterFraction is 0 the result is fully deterministic. A nil Config is promoted to defaults via WithDefaults before computation.

func WithBackoff

func WithBackoff(ctx context.Context, cfg *Config, operation func() error) error

WithBackoff executes operation and retries it up to MaxRetries times on failure, sleeping with exponential backoff between attempts. The retry loop:

  1. Calls operation(). On success (nil error), returns nil immediately.
  2. On failure, computes the backoff delay via CalculateDelay and sleeps. If the context is cancelled during the sleep, returns ctx.Err() instead of continuing.
  3. After exhausting all retries, returns the last error from operation.

A nil Config is promoted to defaults. If the (defaulted) config fails validation, the validation error is returned without ever calling operation.

Example

ExampleWithBackoff shows the minimal configuration needed to retry an operation: pass a partially filled Config (or nil) and zero-value fields are replaced with production defaults.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/open-mrp/api/shared/retry"
)

func main() {
	cfg := &retry.Config{
		MaxRetries:  2,
		InitialWait: 10 * time.Millisecond,
	}

	err := retry.WithBackoff(context.Background(), cfg, func() error {
		return nil // the operation to retry
	})
	fmt.Println(err)
}
Output:
<nil>

Types

type Config

type Config struct {
	// MaxRetries (optional; default: 3) is the maximum number of retry attempts after the initial call. Total invocations = MaxRetries + 1. The zero value is treated as "unset" by WithDefaults and replaced with the default of 3; running exactly once with no retries is not expressible via this config.
	MaxRetries int

	// InitialWait (optional; default: 1s) is the delay before the first retry. It also serves as the absolute floor for the jittered delay — no sleep will ever be shorter than this value.
	InitialWait time.Duration

	// MaxWait (optional; default: 10s) caps the computed exponential delay. Once InitialWait * Multiplier^attempt exceeds MaxWait, the delay is pinned to MaxWait (before jitter).
	MaxWait time.Duration

	// Multiplier (optional; default: 2.0) is the base of the exponential growth applied per attempt. A value of 2.0 doubles the delay each retry; 1.0 produces constant-interval retries. Must be >= 1.0.
	Multiplier float64

	// JitterFraction (optional; default: 0.1) controls the +/- random spread applied to each delay. A value of 0.1 means the final delay is within +/-10% of the computed exponential value. Must be in [0, 1.0]. The zero value is treated as "unset" by WithDefaults and replaced with the default of 0.1, so disabling jitter is only possible when bypassing WithDefaults (e.g. calling CalculateDelay directly).
	JitterFraction float64
}

Config controls the retry behavior of WithBackoff. All fields have sensible production defaults (applied by Config.WithDefaults) so callers only need to set the values they want to override.

func (*Config) WithDefaults

func (c *Config) WithDefaults() *Config

WithDefaults returns a new Config with all zero-value fields replaced by production defaults. It is safe to call on a nil receiver — a nil Config produces a fully-defaulted config. The original Config is not mutated; a copy is always returned.

Jump to

Keyboard shortcuts

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