decorators

package
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Oct 10, 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