Help us shape the future of Go! Take the 2026 Go Developer Survey

decorators

package
v0.3.6 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package decorators provides the shared client-boundary resilience stack: one reusable builder that composes, from outermost to innermost,

Bulkhead → Retry → CircuitBreaker → Timeout → Tracing → Metrics → Logging

around any direct-SDK client operation (S3, SQS, Bedrock, …). It generalizes the pattern proven in clients/storage/decorators (which hand-rolled CB/Timeout/Tracing/Metrics/Logging): each per-client decorator delegates its per-operation work to Run, so every direct-SDK client gets the same protection the RPC/gRPC/repo paths already have.

The layers are nil-able: a nil primitive (or zero timeout) disables that layer, so a bare Stack is a passthrough. Which layers a client gets is chosen by configuration (StackFromConfig); resilience primitives come from go/foundation/resilience behind go/core/interfaces.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Run

func Run[T any](
	ctx context.Context,
	s *Stack,
	op string,
	opts RunOpts,
	fn func(context.Context) (T, error),
) (T, error)

Run executes fn through the stack in the order Bulkhead → Retry → CircuitBreaker → Timeout → Tracing → Metrics → Logging.

The nesting is load-bearing: Retry is OUTSIDE the circuit breaker, so each retry attempt passes through the breaker (it observes per-attempt outcomes and, once open, short-circuits the remaining attempts without invoking fn). The bulkhead holds its slot for the whole retry sequence. It is a free function because Go methods cannot be generic; the result is captured across attempts and the last attempt's value is returned.

func RunStream

func RunStream[T any](
	ctx context.Context,
	s *Stack,
	op string,
	fn func(context.Context) (T, error),
) (val T, cleanup func(), err error)

RunStream runs fn through Bulkhead → CircuitBreaker → Timeout → Tracing → Metrics → Logging for a lazy-stream operation whose returned value must outlive this call (e.g. an S3 GetObject body read AFTER the method returns). Unlike Run, the timeout and span are NOT ended on return — the caller must invoke the returned cleanup exactly once when the stream is closed, so the deadline stays alive for the whole transfer. Retry is never applied to a stream (the body cannot be replayed). On error, cleanup is invoked before returning.

Types

type Config

type Config struct {
	// CircuitBreaker tunes the per-attempt breaker; its Name defaults to the client name.
	CircuitBreaker circuitbreaker.Config `yaml:"circuit_breaker" mapstructure:"circuit_breaker"`

	// Retry tunes the Retry layer; only consumed when RetryEnabled is true.
	Retry retry.Config `yaml:"retry" mapstructure:"retry"`

	// Bulkhead tunes the concurrency limit (the outermost layer).
	Bulkhead bulkhead.Config `yaml:"bulkhead" mapstructure:"bulkhead"`

	// Timeout is the per-attempt deadline; zero leaves the timeout layer off.
	Timeout time.Duration `yaml:"timeout" mapstructure:"timeout"`

	// Enabled gates the whole stack; false yields a bare passthrough (backwards-compatible).
	Enabled bool `yaml:"enabled" mapstructure:"enabled"`

	// RetryEnabled opts the client into the Retry layer (off by default so it never
	// doubles up with an SDK/existing retry authority).
	RetryEnabled bool `yaml:"retry_enabled" mapstructure:"retry_enabled"`
}

Config tunes the client resilience stack for one client tier. It is the minimal per-client surface (expands it): Enabled gates the whole stack, and the primitive sub-configs tune each layer. A Config with Enabled=false yields a bare (passthrough) stack — the backwards-compatible path.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns sensible production defaults: the stack enabled with Bulkhead/CircuitBreaker/Timeout on, and the Retry layer off (opt-in per client so it never doubles up with an SDK/existing retry authority).

type Deps

type Deps struct {
	// Metrics records per-operation counts, errors, and latency.
	Metrics interfaces.Metrics

	// Logger records failed operations.
	Logger interfaces.Logger

	// Tracer emits a span per operation.
	Tracer oteltrace.Tracer
}

Deps carries the injected observability collaborators. Any may be nil, which leaves that layer off.

type RunOpts

type RunOpts struct {
	// Retryable enables the Retry layer for this operation. Body- or
	// stream-consuming and non-idempotent operations (e.g. S3 Upload,
	// CompleteMultipartUpload) must pass false so they are never replayed.
	Retryable bool
}

RunOpts are the per-operation flags for Run.

type Stack

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

Stack composes resilience + observability layers around a client boundary. A nil layer (or zero timeout) is skipped, so the zero-value Stack is a passthrough. Build one with New + the With* options, or StackFromConfig.

func New

func New(name string) *Stack

New creates a bare (passthrough) Stack labelled name in metrics and spans.

func StackFromConfig

func StackFromConfig(name string, cfg Config, deps Deps) (*Stack, error)

StackFromConfig builds a Stack for the client named name from cfg and deps. A disabled config returns a bare passthrough stack (no layers). It fails loudly if a primitive rejects its config.

func (*Stack) WithBulkhead

func (s *Stack) WithBulkhead(b interfaces.Bulkhead) *Stack

WithBulkhead adds the outermost concurrency-limiting layer.

func (*Stack) WithCircuitBreaker

func (s *Stack) WithCircuitBreaker(
	cb interfaces.CircuitBreaker,
) *Stack

WithCircuitBreaker adds the circuit-breaker layer, evaluated per attempt.

func (*Stack) WithLogger

func (s *Stack) WithLogger(l interfaces.Logger) *Stack

WithLogger logs each failed operation with its op name and error.

func (*Stack) WithMetrics

func (s *Stack) WithMetrics(m interfaces.Metrics) *Stack

WithMetrics records per-operation counts, errors, and latency. A nil Metrics leaves the metrics layer off.

func (*Stack) WithRetrier

func (s *Stack) WithRetrier(r interfaces.Retrier) *Stack

WithRetrier adds the Retry layer, applied only to Retryable operations.

func (*Stack) WithTimeout

func (s *Stack) WithTimeout(d time.Duration) *Stack

WithTimeout sets the per-attempt deadline; zero leaves the timeout layer off.

func (*Stack) WithTracer

func (s *Stack) WithTracer(t oteltrace.Tracer) *Stack

WithTracer emits an OpenTelemetry span per operation.

Jump to

Keyboard shortcuts

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