idempotency

package
v2.1.0 Latest Latest
Warning

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

Go to latest
Published: May 2, 2026 License: Apache-2.0 Imports: 19 Imported by: 1

Documentation

Overview

Package idempotency provides stable HTTP idempotency middleware. The middleware buffers responses before replay/storage and is therefore not suitable for streaming, hijacking, HTTP/2 push, or other handlers that rely on optional http.ResponseWriter interfaces. If a completed response cannot be persisted for replay, the middleware fails closed with 503 and stores an ambiguous state for that key instead of reopening it for another execution. Buffered responses that exceed Options.MaxResponseBytes follow the same ambiguous-outcome path. The default request hash includes authenticated actor and tenant scope when earlier middleware has populated them in request context.

Compatibility helpers:

  • Store implementations should preserve ports.IdempotencyReleaser.Release(ctx, key) for v2 source compatibility and add ports.IdempotencyReservationReleaser when they can safely release only the current tokened in-flight reservation. The v3 sunset criteria are: maintained stores pass the token-aware adapter contract tests for token mismatch, missing-token legacy cleanup, completed record preservation, and ambiguous record preservation; mixed-version rollout telemetry shows no tokenless fallback events for the agreed support window; and release notes document migration and rollback expectations for custom stores.
  • OnLegacyInFlightCompatibility or LegacyInFlightCompatibilitySink emits additive mixed-version recovery telemetry with method/path/store correlation for both fallback attempts and outcomes.
  • By default, OnLegacyInFlightCompatibility is a logger sink when unset, so fallback telemetry is always emitted with low-cardinality fields and stable key redaction behavior.
  • Legacy compatibility key values are hashed by default. Set LegacyInFlightCompatibilityRawKey=true to opt in to raw key emission.
  • LegacyInFlightCompatibilityAsync disables request-path coupling to callback execution in high-volume telemetry windows by using one bounded queue and four workers per middleware instance. The queue holds 1024 events, drops new events when full, emits a warning with dropped_events/queue_size, and drains best-effort while the process is running, and emits queued events with cancellation stripped from the first enqueue context so request cancellation does not suppress telemetry delivery. There is no request-path flush or shutdown wait; use a synchronous sink if every telemetry event must be durably observed.
  • LegacyInFlightCompatibilitySampleEvery emits one event per N emitted events and is the preferred low-cost throttle for high-volume mixed-version windows.
  • Logger and KnownInFlightTTLs can be used to run startup checks for mixed- version InFlightTTL alignment. Set FailOnInFlightTTLMismatch to fail-fast when rollout rules require strict enforcement.
  • FailOnInFlightClockSkewPreflight enables strict startup governance for clock-skew sensitive startup preflights while preserving advisory mode by default.
  • Use LegacyInFlightCompatibilityMetricSink for metrics-first consumers; event labels are exposed through MetricLabels() and use a stable schema: method, path, store_type, outcome, key, and error.

Recommended event contract:

- `legacy_in_flight_fallback_entered` - `legacy_in_flight_fallback_recovered` - `legacy_in_flight_fallback_rejected` - `legacy_in_flight_fallback_unknown`

Each event should include method/path/store_type/outcome plus an optional key and error payload when failures occur.

Practical guidance:

  • Keep event key hashed by default to limit cardinality.
  • Set LegacyInFlightCompatibilitySampleEvery when compatibility traffic becomes noisy during mixed-version windows to lower event volume deterministically before the bounded async queue sees the event.
  • Prefer an explicit metric sink for dashboard counters and keep logger sink in place during transition.
  • If you rely on request latency, avoid heavy synchronous callback work unless LegacyInFlightCompatibilityAsync is enabled.
  • Treat async compatibility telemetry as lossy operational evidence. It must never be the only source of correctness for idempotency recovery decisions, and raw keys should stay disabled in production unless a short, access- controlled incident review requires them.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrLegacyInFlightTTLMismatch            = errors.New("idempotency in-flight ttl mismatch in rollout contract")
	ErrLegacyInFlightClockSkewPreflightRisk = errors.New("idempotency legacy in-flight clock preflight risk")
)

Functions

func DefaultHash

func DefaultHash(r *http.Request, body []byte) (string, error)

DefaultHash returns a stable SHA-256 hash of caller scope, method, path, query, content type, and body. Authenticated actor and tenant context are included when present.

Types

type HashFunc

type HashFunc func(*http.Request, []byte) (string, error)

HashFunc computes a request hash used to detect key reuse with different payloads.

type KeyFunc

type KeyFunc func(*http.Request) string

KeyFunc extracts an idempotency key from the request.

type LegacyInFlightCompatibilityEvent added in v2.1.0

type LegacyInFlightCompatibilityEvent struct {
	Method    string
	Path      string
	Key       string
	StoreType string
	Outcome   LegacyInFlightCompatibilityEventName
	Error     string
}

LegacyInFlightCompatibilityEvent contains structured fields for idempotency compatibility telemetry.

func (LegacyInFlightCompatibilityEvent) MetricLabels added in v2.1.0

func (event LegacyInFlightCompatibilityEvent) MetricLabels() map[string]string

MetricLabels returns the canonical metric label set for compatibility telemetry. Metrics intentionally expose only bounded labels. High-cardinality event details such as raw paths, key hashes, raw keys, and error strings remain on LegacyInFlightCompatibilityEvent for structured logs or traces.

type LegacyInFlightCompatibilityEventName added in v2.1.0

type LegacyInFlightCompatibilityEventName string

LegacyInFlightCompatibilityEventName identifies structured compatibility events for mixed-version in-flight migrations.

const (
	// LegacyInFlightCompatibilityEntered reports that legacy fallback was considered.
	LegacyInFlightCompatibilityEntered LegacyInFlightCompatibilityEventName = "legacy_in_flight_fallback_entered"
	// LegacyInFlightCompatibilityRecovered reports legacy fallback was successful.
	LegacyInFlightCompatibilityRecovered LegacyInFlightCompatibilityEventName = "legacy_in_flight_fallback_recovered"
	// LegacyInFlightCompatibilityRejected reports fallback was explicitly rejected.
	LegacyInFlightCompatibilityRejected LegacyInFlightCompatibilityEventName = "legacy_in_flight_fallback_rejected"
	// LegacyInFlightCompatibilityUnknown reports fallback ended with an unexpected error.
	LegacyInFlightCompatibilityUnknown LegacyInFlightCompatibilityEventName = "legacy_in_flight_fallback_unknown"
)

type LegacyInFlightCompatibilityEventSink added in v2.1.0

type LegacyInFlightCompatibilityEventSink interface {
	Emit(context.Context, LegacyInFlightCompatibilityEvent)
}

LegacyInFlightCompatibilityEventSink emits telemetry from legacy compatibility events.

Implementations should avoid blocking and avoid panicking, since compatibility telemetry must not alter request behavior.

type LegacyInFlightCompatibilityHandler added in v2.1.0

type LegacyInFlightCompatibilityHandler func(context.Context, LegacyInFlightCompatibilityEvent)

LegacyInFlightCompatibilityHandler receives telemetry from legacy recovery paths.

type LegacyInFlightCompatibilityMetricLabels added in v2.1.0

type LegacyInFlightCompatibilityMetricLabels map[string]string

LegacyInFlightCompatibilityMetricLabels is the canonical compatibility label set for metric adapters.

type LegacyInFlightCompatibilityMetricSink added in v2.1.0

type LegacyInFlightCompatibilityMetricSink interface {
	Emit(context.Context, LegacyInFlightCompatibilityMetricLabels)
}

LegacyInFlightCompatibilityMetricSink emits structured compatibility metrics.

Implementations should avoid blocking and avoid panicking, since compatibility telemetry must not alter request behavior.

type LegacyInFlightCompatibilityMetricSinkFunc added in v2.1.0

type LegacyInFlightCompatibilityMetricSinkFunc func(context.Context, LegacyInFlightCompatibilityMetricLabels)

LegacyInFlightCompatibilityMetricSinkFunc adapts a function to LegacyInFlightCompatibilityMetricSink.

func (LegacyInFlightCompatibilityMetricSinkFunc) Emit added in v2.1.0

type LegacyInFlightCompatibilitySinkFunc added in v2.1.0

type LegacyInFlightCompatibilitySinkFunc func(context.Context, LegacyInFlightCompatibilityEvent)

LegacyInFlightCompatibilitySinkFunc adapts a function to LegacyInFlightCompatibilityEventSink.

func (LegacyInFlightCompatibilitySinkFunc) Emit added in v2.1.0

type Middleware

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

Middleware enforces Idempotency-Key semantics.

func New

func New(opts Options) (*Middleware, error)

New constructs an idempotency middleware.

func (*Middleware) Handler

func (m *Middleware) Handler(next http.Handler) http.Handler

Handler wraps the next handler with idempotency logic.

func (*Middleware) Middleware

func (m *Middleware) Middleware() func(http.Handler) http.Handler

Middleware implements ports.Middleware via Handler adapter.

type Options

type Options struct {
	Store               ports.IdempotencyStore
	HeaderName          string
	KeyFunc             KeyFunc
	HashFunc            HashFunc
	TTL                 time.Duration
	InFlightTTL         time.Duration
	MaxBodyBytes        int64
	MaxResponseBytes    int64
	Clock               ports.Clock
	ShouldHandle        func(*http.Request) bool
	ShouldStore         func(status int) bool
	ResponseHeaderAllow []string
	ResponseHeaderDeny  []string
	ReplayHeaderName    string
	FailOpen            bool
	OnError             func(error)
	Logger              ports.Logger
	// KnownInFlightTTLs maps discovered peers to their observed in-flight TTL.
	KnownInFlightTTLs map[string]time.Duration
	// FailOnInFlightTTLMismatch enables hard-fail on TTL mismatch during startup.
	FailOnInFlightTTLMismatch bool
	// FailOnInFlightClockSkewPreflight promotes startup clock-skew risk checks into hard-fail.
	FailOnInFlightClockSkewPreflight bool
	// LegacyInFlightCompatibilityRawKey exposes the raw request key in compatibility events.
	// Defaults to false (hashed key output). Keep this disabled in production
	// unless a short, access-controlled incident review needs exact keys.
	LegacyInFlightCompatibilityRawKey bool
	// LegacyInFlightCompatibilitySink emits compatibility telemetry independently of the
	// legacy callback contract. Use this for logging adapters or custom integrations.
	LegacyInFlightCompatibilitySink LegacyInFlightCompatibilityEventSink
	// LegacyInFlightCompatibilityMetricSink emits metric label sets for compatibility events.
	// Use this for Prometheus/observability pipelines that prefer canonical label contracts.
	LegacyInFlightCompatibilityMetricSink LegacyInFlightCompatibilityMetricSink
	// LegacyInFlightCompatibilityAsync avoids blocking request execution for telemetry
	// work by enqueueing events to a bounded worker. When the queue is full,
	// events are dropped and a warning is emitted; request execution is not
	// backpressured.
	LegacyInFlightCompatibilityAsync bool
	// LegacyInFlightCompatibilitySampleEvery emits one event per N emitted events for
	// high-volume environments. Values <= 1 preserve full event output. Use this
	// to reduce async queue pressure and metric/log volume. Cardinality remains
	// bounded by key hashing defaults unless RawKey is enabled.
	LegacyInFlightCompatibilitySampleEvery int
	// OnLegacyInFlightCompatibility receives additive mixed-version telemetry.
	OnLegacyInFlightCompatibility LegacyInFlightCompatibilityHandler
}

Options configures the idempotency middleware.

Jump to

Keyboard shortcuts

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