Skip to main content

Overview

The retry middleware automatically retries failed requests with configurable backoff strategies, handling transient failures gracefully. Use it when you need:
  • Handle temporary network issues
  • Retry on specific error codes
  • Implement exponential backoff

Installation

import "github.com/go-mizu/mizu/middlewares/retry"

Quick Start

app := mizu.New()

// Retry up to 3 times on 5xx errors
app.Use(retry.New(3))

Configuration

Options

OptionTypeDefaultDescription
MaxRetriesint3Maximum retry attempts
Delaytime.Duration100msInitial delay
MaxDelaytime.Duration10sMaximum delay
Multiplierfloat642.0Backoff multiplier
RetryOn[]int[500-599]Status codes to retry
RetryIffunc(*mizu.Ctx, error) bool-Custom retry condition

Examples

Basic Retry

app.Use(retry.New(3))

With Backoff

app.Use(retry.WithOptions(retry.Options{
    MaxRetries: 5,
    Delay:      100 * time.Millisecond,
    MaxDelay:   5 * time.Second,
    Multiplier: 2.0, // Exponential backoff
}))

Specific Status Codes

app.Use(retry.WithOptions(retry.Options{
    MaxRetries: 3,
    RetryOn:    []int{502, 503, 504}, // Gateway errors only
}))

Custom Condition

app.Use(retry.WithOptions(retry.Options{
    MaxRetries: 3,
    RetryIf: func(c *mizu.Ctx, err error) bool {
        // Retry on timeout errors
        return errors.Is(err, context.DeadlineExceeded)
    },
}))

Constant Delay

app.Use(retry.WithOptions(retry.Options{
    MaxRetries: 3,
    Delay:      1 * time.Second,
    Multiplier: 1.0, // No increase
}))

API Reference

Functions

// New creates retry middleware
func New(maxRetries int) mizu.Middleware

// WithOptions creates with configuration
func WithOptions(opts Options) mizu.Middleware

Backoff Calculation

delay = min(initialDelay * (multiplier ^ attempt), maxDelay)
Example with defaults:
  • Attempt 1: 100ms
  • Attempt 2: 200ms
  • Attempt 3: 400ms

Technical Details

Implementation Architecture

The retry middleware uses a custom retryResponseWriter wrapper that intercepts response status codes to determine if a retry should occur. Key implementation details:
  • Response Writer Wrapping: Each retry attempt wraps the response writer to capture the status code without committing the response until success or max retries reached
  • Exponential Backoff Algorithm: Delay calculation follows the formula: delay = min(initialDelay * (multiplier ^ attempt), maxDelay)
  • Retry Decision Logic: The RetryIf function receives the context, error, and current attempt number to make intelligent retry decisions
  • State Management: The middleware tracks attempt count, last error, and current delay across retry iterations

Default Behavior

When using New() without options, the middleware:
  • Retries up to 3 times (4 total attempts including initial)
  • Starts with 100ms delay
  • Uses 2.0x multiplier for exponential backoff
  • Caps maximum delay at 1 second
  • Retries on any error or 5xx HTTP status codes

Helper Functions

The package provides several helper functions for common retry patterns:
  • RetryOn(codes ...int): Creates a RetryIf function that retries only on specific HTTP status codes
  • RetryOnError(): Creates a RetryIf function that retries only when an error is returned
  • NoRetry(): Creates a RetryIf function that disables retries (useful for testing)

Performance Considerations

  • The middleware sleeps between retries using time.Sleep, blocking the goroutine
  • Response writer wrapping adds minimal overhead
  • Status code capture happens in memory without additional allocations
  • The OnRetry callback allows for logging and metrics without impacting retry logic

Best Practices

  • Use exponential backoff to prevent thundering herd
  • Set reasonable max retries (3-5)
  • Only retry idempotent operations
  • Add jitter for distributed systems
  • Log retry attempts for debugging

Testing

Test Coverage

The retry middleware includes comprehensive test cases covering various scenarios:
Test CaseDescriptionExpected Behavior
TestNewBasic retry with default optionsRetries up to 3 times on errors, succeeds on 3rd attempt
TestWithOptions_MaxRetriesCustom max retries configurationRespects MaxRetries setting (1 initial + 2 retries = 3 attempts)
TestWithOptions_OnRetryOnRetry callback invocationOnRetry called before each retry attempt (not on initial attempt)
TestWithOptions_NoRetryNeededSuccessful request on first attemptNo retries when request succeeds immediately
TestWithOptions_CustomRetryIfCustom retry condition logicCustom RetryIf function controls retry behavior independently of MaxRetries
TestRetryOnRetryOn helper for specific status codesRetries only on specified status codes (500, 502, 503)
TestRetryOnErrorRetryOnError helper functionRetries only when error is present, not on status codes
TestNoRetryNoRetry helper functionNever retries regardless of error or status code
TestWithOptions_ExponentialBackoffExponential backoff timingDelays increase exponentially between retry attempts