Skip to main content

Overview

The hedge middleware sends parallel backup requests after a delay, returning the first successful response. This reduces tail latency by hedging against slow responses. Use it when you need:
  • Reduce p99 latency
  • Handle slow backend responses
  • Improve user experience

Installation

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

Quick Start

app := mizu.New()

// Send hedge request after 100ms
app.Use(hedge.New(100 * time.Millisecond))

Configuration

Options

OptionTypeDefaultDescription
Delaytime.DurationRequiredTime before hedge
MaxHedgesint1Max parallel hedges
Skipfunc(*mizu.Ctx) boolnilSkip hedging

Examples

Basic Hedging

// Hedge after 200ms
app.Use(hedge.New(200 * time.Millisecond))

Multiple Hedges

app.Use(hedge.WithOptions(hedge.Options{
    Delay:     100 * time.Millisecond,
    MaxHedges: 2, // Up to 2 parallel requests
}))

Skip Non-Idempotent

app.Use(hedge.WithOptions(hedge.Options{
    Delay: 100 * time.Millisecond,
    Skip: func(c *mizu.Ctx) bool {
        // Don't hedge mutations
        return c.Request().Method != http.MethodGet
    },
}))

How It Works

  1. Request arrives
  2. Start processing
  3. After delay, if no response, send hedge request
  4. Return first successful response
  5. Cancel other requests

API Reference

Functions

// New creates hedge middleware
func New(delay time.Duration) mizu.Middleware

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

When to Use

  • High-latency backends
  • P99 latency optimization
  • Read-heavy workloads

When NOT to Use

  • Non-idempotent operations (POST, PUT, DELETE)
  • Resource-intensive operations
  • When backend can’t handle extra load

Technical Details

Implementation

The hedge middleware uses a sophisticated concurrent request pattern:
  1. Request Buffering: The middleware reads and buffers the request body to enable multiple identical requests
  2. Response Recording: Each request (original and hedges) writes to a responseRecorder that captures headers, status code, and body
  3. Atomic Winner Selection: Uses atomic operations (CompareAndSwapInt32) to ensure only the first completing request wins
  4. Context Management: Each request receives hedge metadata through context values (HedgeInfo)
  5. Graceful Cancellation: When a winner is selected, remaining requests are cancelled via context

Key Components

  • Hedger: Main struct managing options and statistics
  • Options: Configuration including delay, max hedges, timeout, and callbacks
  • HedgeInfo: Context data containing hedge number, total hedges, winner, and duration
  • Stats: Tracks total requests, hedged requests, hedges triggered, and win rates
  • responseRecorder: Custom http.ResponseWriter that buffers responses

Statistics Tracking

The middleware tracks comprehensive statistics:
  • Total requests processed
  • Requests eligible for hedging
  • Number of hedges actually triggered
  • Wins by original vs hedged requests

Callbacks

Two callback hooks are available:
  • OnHedge: Called when a hedge request is triggered
  • OnComplete: Called when the winning response is selected

Default Values

SettingDefault ValuePurpose
Delay100msTime before triggering hedge
MaxHedges1Maximum concurrent hedges
Timeout30sOverall request timeout

Best Practices

  • Only use for idempotent operations
  • Set delay based on p50 latency
  • Monitor hedge rate
  • Ensure backend can handle increased load

Testing

The hedge middleware includes comprehensive test coverage:
Test CaseDescriptionExpected Behavior
TestNewBasic middleware creationMiddleware works with default options
TestFastResponseFast response before hedge delayOriginal request completes, no hedge triggered
TestHedgeTriggeredSlow response triggers hedgeHedge request is started after delay
TestShouldHedgeConditional hedging based on pathOnly specified paths are hedged
TestOnCompleteOnComplete callback executionCallback receives hedge number and duration
TestStatsStatistics trackingTracks total requests and hedged requests
TestGetHedgeInfoRetrieve hedge metadata from contextReturns HedgeInfo with hedge number
TestIsHedgeCheck if request is a hedgeOriginal request returns false
TestConditionalConditional middleware for specific methodsOnly GET requests are hedged
TestForSlowRequestsHedge slow requests with thresholdHedges triggered after custom delay
TestMaxHedgesMultiple concurrent hedgesUp to MaxHedges requests run in parallel
TestTimeoutRequest timeout handlingRequests timeout after configured duration