resilix

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Mar 24, 2026 License: MIT Imports: 7 Imported by: 0

README

resilix

Go Reference Go Report Card

Composable, type-safe resilience primitives for Go services.

Zero external dependencies in the core library. Optional OpenTelemetry integration ships as a separate subpackage.


What is resilix?

resilix is a lightweight Go library that provides battle-tested resilience patterns to protect your services from cascading failures. When an upstream API goes down, a database slows to a crawl, or a third-party service starts timing out — resilix keeps your application responsive and degrading gracefully instead of collapsing.

It ships four core primitives — Circuit Breaker, Retry, Bulkhead, and Timeout — that can be used standalone or composed into a Pipeline for layered protection. Everything is built with Go generics, giving you full type safety without sacrificing ergonomics.

Why resilix?
  • No framework lock-in — resilix is a library, not a framework. Drop it into any Go project.
  • Generics-first APIExecute[T] and WithFallback[T] return typed results directly. No interface{} boxing, no type assertions.
  • Zero core dependencies — the core library imports only the Go standard library. The OpenTelemetry integration is an optional subpackage (resilix/otel).
  • Production-ready defaultsDefaultPipeline("my-service") gives you a sensible Timeout → Retry → Circuit Breaker chain in a single line.
  • Fully testable — inject FakeClock to make time-dependent tests instant and deterministic.

Features

Primitive Description
Circuit Breaker Trips when failure rate exceeds threshold; recovers via probe calls in half-open state
Retry Re-executes on failure with pluggable backoff strategies and predicates
Bulkhead Limits concurrent calls to protect goroutine pools from slow downstreams
Timeout Cancels calls exceeding a deadline with no goroutine leaks
Pipeline Chains primitives in order; generic Execute[T] for fully type-safe results
Highlights
  • Generics-firstExecute[T] and WithFallback[T] return typed results, no boxing or type assertions
  • Composable — mix and match primitives via Pipeline, or use them standalone
  • Observable — plug in OpenTelemetry, Prometheus, or any custom Observer implementation
  • TestableFakeClock makes time-dependent tests fast and deterministic
  • Zero dependencies — core library has no external deps; OTel integration is opt-in

Installation

go get github.com/ChiragRayani/resilix

Requires Go 1.25+ (generics).


Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ChiragRayani/resilix"
)

func main() {
    // One-liner with sane defaults:
    // Timeout(5s) → Retry(3 attempts, exp backoff) → CircuitBreaker(50%, 30s)
    pipeline := resilix.DefaultPipeline("my-api")

    // Generic execute — full type safety, no boxing
    result, err := resilix.Execute(context.Background(), pipeline,
        func(ctx context.Context) (string, error) {
            return "hello from upstream!", nil
        },
    )
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result) // "hello from upstream!"
}

Custom Pipeline

Build a pipeline tailored to your downstream:

pipeline := resilix.NewPipeline("payment-service",
    resilix.NewTimeout(resilix.TimeoutConfig{
        Duration: 3 * time.Second,
    }),
    resilix.NewRetry(resilix.RetryConfig{
        MaxAttempts: 3,
        Backoff:     resilix.ExponentialBackoffWithJitter(100*time.Millisecond, 2.0, 5*time.Second, 0.2),
        RetryIf:     myRetryPredicate,
    }),
    resilix.NewCircuitBreaker(resilix.CBConfig{
        FailureThreshold: 0.5,
        MinRequests:      5,
        OpenTimeout:      30 * time.Second,
        Window:           resilix.NewSlidingWindow(60*time.Second, 6),
    }),
    resilix.NewBulkhead(resilix.BulkheadConfig{
        MaxConcurrent: 20,
        WaitTimeout:   500 * time.Millisecond,
    }),
)

Policy Ordering

Recommended ordering (outermost → innermost):

Timeout → Retry → CircuitBreaker → Bulkhead
Position Policy Rationale
Outermost Timeout Governs the entire operation including all retries
Retry Re-attempts before hitting the circuit breaker each time
Circuit Breaker Short-circuits early when the downstream is known-bad
Innermost Bulkhead Limits simultaneous in-flight calls to the downstream

Standalone Usage

Each primitive can also be used independently:

Circuit Breaker
cb := resilix.NewCircuitBreaker(resilix.CBConfig{
    FailureThreshold: 0.5,
    MinRequests:      5,
    OpenTimeout:      30 * time.Second,
    Window:           resilix.NewCountWindow(10),
})

err := cb.ExecuteCB(ctx, func(ctx context.Context) error {
    return callDownstream(ctx)
})
Retry
r := resilix.NewRetry(resilix.RetryConfig{
    MaxAttempts: 3,
    Backoff:     resilix.ExponentialBackoffWithJitter(100*time.Millisecond, 2.0, 10*time.Second, 0.2),
    RetryIf:     resilix.RetryAll,
})

err := r.ExecuteRetry(ctx, func(ctx context.Context) error {
    return callDownstream(ctx)
})
Bulkhead
bh := resilix.NewBulkhead(resilix.BulkheadConfig{
    MaxConcurrent: 20,
    WaitTimeout:   500 * time.Millisecond,
})

err := bh.ExecuteBulkhead(ctx, func(ctx context.Context) error {
    return callDownstream(ctx)
})
Timeout
to := resilix.NewTimeout(resilix.TimeoutConfig{
    Duration: 3 * time.Second,
})

err := to.ExecuteTimeout(ctx, func(ctx context.Context) error {
    return callDownstream(ctx)
})

Failure Windows

Two implementations ship out of the box for the circuit breaker's failure tracking:

// Count-based: tracks last N calls. Lightest option.
resilix.NewCountWindow(10)

// Sliding time-based: 60s window split into 6 × 10s buckets. More accurate under variable load.
resilix.NewSlidingWindow(60*time.Second, 6)

// Atomic counter: tracks consecutive failures. Use for "N consecutive failures" semantics.
// (implements FailureWindow)
&resilix.AtomicCounter{}

Implement the resilix.FailureWindow interface to plug in your own strategy (e.g., a Google SRE-style adaptive throttle).


Backoff Strategies

// Constant: same wait before every retry
resilix.ConstantBackoff(500 * time.Millisecond)

// Exponential: base * multiplier^(attempt-1), capped at max
resilix.ExponentialBackoff(100*time.Millisecond, 2.0, 10*time.Second)

// Exponential with jitter: ±20% random jitter to prevent thundering herd
resilix.ExponentialBackoffWithJitter(100*time.Millisecond, 2.0, 10*time.Second, 0.2)

// Linear: increases wait by step on each attempt
resilix.LinearBackoff(100*time.Millisecond, 200*time.Millisecond, 5*time.Second)

Retry Predicates

Control which errors trigger a retry:

// Retry any non-nil error (default)
resilix.RetryAll

// Never retry
resilix.RetryNone

// Retry only specific error types
resilix.RetryOn(mySpecificError)

// Compose predicates with OR semantics
resilix.RetryIf(
    resilix.RetryOn(errTimeout),
    resilix.RetryOn(errServiceUnavailable),
)

Fallback / Graceful Degradation

product, err := resilix.WithFallback(ctx, pipeline,
    func(ctx context.Context) (*Product, error) {
        return api.Get(ctx, id)
    },
    func(ctx context.Context, err error) (*Product, error) {
        return cache.Get(ctx, id) // serve stale data
    },
)

Observability

OpenTelemetry Integration
import "github.com/ChiragRayani/resilix/otel"

obs, err := otel.NewObserver(meter, tracer)
if err != nil {
    log.Fatal(err)
}

cb := resilix.NewCircuitBreaker(resilix.CBConfig{
    Observer: obs,
    // ...other config
})

Recorded metrics:

Metric Type Description
resilix.executions Counter Total calls, labelled by policy and result
resilix.rejections Counter Calls rejected without execution
resilix.latency Histogram Execution latency per policy (seconds)
resilix.retries Counter Retry attempts per policy
resilix.state_changes Counter Circuit breaker state transitions
Custom Observer

Implement the resilix.Observer interface to wire in Prometheus, structured logs, or any other system:

type Observer interface {
    OnStateChange(policy string, from, to State)
    OnSuccess(policy string, latency time.Duration)
    OnFailure(policy string, err error, latency time.Duration)
    OnRejected(policy string)
    OnRetry(policy string, attempt int, err error)
}

Testing

Use testutil.FakeClock to control time in circuit breaker and backoff tests — no time.Sleep required:

import "github.com/ChiragRayani/resilix/testutil"

clock := testutil.NewFakeClock(time.Now())
cb := resilix.NewCircuitBreaker(resilix.CBConfig{
    Clock:       clock,
    OpenTimeout: 30 * time.Second,
})

// ... trip the breaker ...

// Jump past the timeout instantly — no waiting
clock.Advance(31 * time.Second)
// cb.State() == StateHalfOpen ✓

Run the test suite:

go test ./... -v

Sentinel Errors

Error Returned when
ErrCircuitOpen Call rejected because the circuit breaker is open
ErrBulkheadFull No execution slot available in the bulkhead
ErrMaxRetriesExceeded All retry attempts exhausted (wraps the last error)
ErrTimeout Execution exceeded its deadline

All errors support errors.Is and errors.Unwrap for idiomatic Go error handling.


Project Structure

resilix/
├── types.go              # Core interfaces (Policy, Observer, Clock, FailureWindow) & errors
├── circuit_breaker.go    # Circuit Breaker implementation
├── retry.go              # Retry with pluggable backoff & predicates
├── bulkhead.go           # Semaphore-based concurrency limiter
├── timeout.go            # Context-based timeout with goroutine leak prevention
├── pipeline.go           # Pipeline, generic Execute[T], WithFallback[T], DefaultPipeline
├── window.go             # CountWindow, SlidingWindow, AtomicCounter
├── otel/
│   └── observer.go       # OpenTelemetry Observer implementation
├── testutil/
│   ├── fake_clock.go     # FakeClock for deterministic time control
│   └── circuit_breaker_test.go  # Comprehensive test suite
├── examples/
│   └── basic/main.go     # Real-world HTTP client example
├── go.mod
└── README.md

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License — see the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrBulkheadFull = &policyError{msg: "bulkhead is full"}

ErrBulkheadFull is returned when no execution slot is available.

View Source
var ErrCircuitOpen = &policyError{msg: "circuit breaker is open"}

ErrCircuitOpen is returned when a call is rejected because the circuit is open.

View Source
var ErrMaxRetriesExceeded = &policyError{msg: "max retries exceeded", wrapped: nil}

ErrMaxRetriesExceeded is returned when all retry attempts are exhausted.

View Source
var ErrTimeout = &policyError{msg: "execution timed out"}

ErrTimeout is returned when an execution exceeds its deadline.

Functions

func Execute

func Execute[T any](ctx context.Context, policy Policy, fn func(context.Context) (T, error)) (T, error)

Execute runs fn through policy, returning a typed result. policy can be a *CircuitBreaker, *Retry, *Bulkhead, *Timeout, or *Pipeline.

Example:

result, err := resilix.Execute(ctx, pipeline, func(ctx context.Context) (MyResult, error) {
    return client.Call(ctx)
})

func RetryAll

func RetryAll(err error, _ int) bool

RetryAll retries any non-nil error.

func RetryNone

func RetryNone(_ error, _ int) bool

RetryNone never retries.

func WithFallback

func WithFallback[T any](
	ctx context.Context,
	policy Policy,
	fn func(context.Context) (T, error),
	fallback func(context.Context, error) (T, error),
) (T, error)

WithFallback runs fn through policy. If the policy returns an error, fallback is called with that error to produce a default value. This is useful for graceful degradation.

Types

type AtomicCounter

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

AtomicCounter is the simplest possible failure counter: just a running total, reset on success. Use when you want "N consecutive failures" semantics rather than a rate.

func (*AtomicCounter) ConsecutiveFailures

func (c *AtomicCounter) ConsecutiveFailures() int64

ConsecutiveFailures returns the current consecutive failure count.

func (*AtomicCounter) FailureRate

func (c *AtomicCounter) FailureRate() float64

func (*AtomicCounter) Record

func (c *AtomicCounter) Record(success bool)

func (*AtomicCounter) Reset

func (c *AtomicCounter) Reset()

func (*AtomicCounter) Total

func (c *AtomicCounter) Total() int64

type BackoffStrategy

type BackoffStrategy func(attempt int) time.Duration

BackoffStrategy returns the wait duration before attempt number `attempt` (1-indexed).

func ConstantBackoff

func ConstantBackoff(d time.Duration) BackoffStrategy

ConstantBackoff waits the same duration before every retry.

func ExponentialBackoff

func ExponentialBackoff(base time.Duration, multiplier float64, max time.Duration) BackoffStrategy

ExponentialBackoff implements base * multiplier^(attempt-1), capped at max. Pass max=0 to disable the cap.

func ExponentialBackoffWithJitter

func ExponentialBackoffWithJitter(base time.Duration, multiplier float64, max time.Duration, jitterFraction float64) BackoffStrategy

ExponentialBackoffWithJitter adds ±jitterFraction random jitter to ExponentialBackoff to avoid thundering herd on retry storms. jitterFraction should be in (0, 1]; 0.2 means ±20%.

func LinearBackoff

func LinearBackoff(initial, step time.Duration, max time.Duration) BackoffStrategy

LinearBackoff increases wait by `step` on each attempt.

type Bulkhead

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

Bulkhead limits the number of concurrent executions to prevent a slow downstream from monopolising the caller's goroutine pool.

It is backed by a buffered channel used as a semaphore — zero allocations per call once the channel is created.

func NewBulkhead

func NewBulkhead(cfg BulkheadConfig) *Bulkhead

NewBulkhead creates a Bulkhead with the given config.

func (*Bulkhead) Available

func (b *Bulkhead) Available() int

Available returns the number of free execution slots.

func (*Bulkhead) ExecuteBulkhead

func (b *Bulkhead) ExecuteBulkhead(ctx context.Context, fn func(context.Context) error) error

ExecuteBulkhead runs fn if a slot is available; otherwise it either waits (if WaitTimeout > 0) or returns ErrBulkheadFull immediately.

func (*Bulkhead) Name

func (b *Bulkhead) Name() string

Name implements Policy.

type BulkheadConfig

type BulkheadConfig struct {
	// Name identifies this policy in metrics.
	Name string

	// MaxConcurrent is the maximum number of calls that may execute simultaneously.
	// Default: 10.
	MaxConcurrent int

	// WaitTimeout is how long a caller will wait for a slot before receiving
	// ErrBulkheadFull. Use 0 for fail-fast (never queue).
	// Default: 0 (fail fast).
	WaitTimeout time.Duration

	// Observer receives rejected and success events.
	Observer Observer

	// Clock is used for timeout tracking. Override in tests.
	Clock Clock
}

BulkheadConfig configures a Bulkhead.

type CBConfig

type CBConfig struct {
	// Name identifies this breaker in metrics and logs.
	Name string

	// FailureThreshold is the failure rate [0,1] at which the circuit trips.
	// Default: 0.5 (50% failure rate trips the breaker).
	FailureThreshold float64

	// MinRequests is the minimum number of calls in the window before the
	// failure rate is evaluated. Prevents tripping on a single early failure.
	// Default: 5.
	MinRequests int64

	// OpenTimeout is how long the circuit stays open before moving to half-open.
	// Default: 30s.
	OpenTimeout time.Duration

	// HalfOpenMax is how many probe calls are allowed in the half-open state.
	// Default: 1.
	HalfOpenMax int64

	// Window is the failure tracking strategy. Defaults to CountWindow(10).
	Window FailureWindow

	// Observer receives state change and execution events.
	Observer Observer

	// Clock is used for time. Defaults to the real clock.
	// Override in tests with testutil.NewFakeClock().
	Clock Clock
}

CBConfig configures a CircuitBreaker.

type CircuitBreaker

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

CircuitBreaker implements the classic three-state circuit breaker pattern.

Use Execute or ExecuteCB to run calls through it. CircuitBreaker is safe for concurrent use.

func NewCircuitBreaker

func NewCircuitBreaker(cfg CBConfig) *CircuitBreaker

NewCircuitBreaker creates a new CircuitBreaker with the given config.

func (*CircuitBreaker) ExecuteCB

func (cb *CircuitBreaker) ExecuteCB(ctx context.Context, fn func(context.Context) error) error

ExecuteCB runs fn through the circuit breaker, returning ErrCircuitOpen if the circuit is open. Use the generic Execute[T] function for type-safe results.

func (*CircuitBreaker) Name

func (cb *CircuitBreaker) Name() string

Name implements Policy.

func (*CircuitBreaker) Reset

func (cb *CircuitBreaker) Reset()

Reset forces the circuit breaker back to the closed state and clears the window.

func (*CircuitBreaker) State

func (cb *CircuitBreaker) State() State

State returns the current circuit state. Safe to call concurrently.

type Clock

type Clock interface {
	Now() time.Time
	Since(t time.Time) time.Duration
}

Clock abstracts time so that tests can control it.

type CountWindow

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

CountWindow uses a simple rolling count of the last N calls. It is the lightest option and suitable when call rate is stable.

func NewCountWindow

func NewCountWindow(size int) *CountWindow

NewCountWindow returns a window that tracks the last `size` calls.

func (*CountWindow) FailureRate

func (w *CountWindow) FailureRate() float64

func (*CountWindow) Record

func (w *CountWindow) Record(success bool)

func (*CountWindow) Reset

func (w *CountWindow) Reset()

func (*CountWindow) Total

func (w *CountWindow) Total() int64

type FailureWindow

type FailureWindow interface {
	// Record registers one result. success=true on a successful call.
	Record(success bool)
	// FailureRate returns a value in [0, 1].
	FailureRate() float64
	// Reset clears all accumulated data.
	Reset()
	// Total returns the total number of calls recorded.
	Total() int64
}

FailureWindow tracks the recent failure rate used by the circuit breaker. Swap in a different implementation for sliding-window or count-based semantics.

type NoopObserver

type NoopObserver struct{}

NoopObserver is a zero-allocation observer that discards all events. It is the default when no observer is configured.

func (NoopObserver) OnFailure

func (NoopObserver) OnFailure(_ string, _ error, _ time.Duration)

func (NoopObserver) OnRejected

func (NoopObserver) OnRejected(_ string)

func (NoopObserver) OnRetry

func (NoopObserver) OnRetry(_ string, _ int, _ error)

func (NoopObserver) OnStateChange

func (NoopObserver) OnStateChange(_ string, _, _ State)

func (NoopObserver) OnSuccess

func (NoopObserver) OnSuccess(_ string, _ time.Duration)

type Observer

type Observer interface {
	// OnStateChange is called when a circuit breaker transitions between states.
	OnStateChange(policy string, from, to State)

	// OnSuccess is called after a successful execution.
	OnSuccess(policy string, latency time.Duration)

	// OnFailure is called after a failed execution (counted against the threshold).
	OnFailure(policy string, err error, latency time.Duration)

	// OnRejected is called when a call is rejected without being attempted.
	// This happens when the circuit is open, the bulkhead is full, etc.
	OnRejected(policy string)

	// OnRetry is called before each retry attempt (attempt starts at 1).
	OnRetry(policy string, attempt int, err error)
}

Observer receives events from any resilience primitive. Implement this to wire in OpenTelemetry, Prometheus, structured logs, etc.

type Pipeline

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

Pipeline chains multiple policies. Policies are applied in order: the first policy in the slice is the outermost wrapper.

Recommended ordering (outermost → innermost):

Timeout → Retry → CircuitBreaker → Bulkhead

This means: the timeout governs the entire operation including retries; the circuit breaker is checked before each attempt; the bulkhead gates each individual call.

func DefaultPipeline

func DefaultPipeline(name string) *Pipeline

DefaultPipeline builds a sensible pipeline for a single downstream with sane defaults. Good for getting started quickly.

Timeout(5s) → Retry(3 attempts, exp backoff) → CircuitBreaker(50% rate, 30s)

func NewPipeline

func NewPipeline(name string, policies ...Policy) *Pipeline

NewPipeline creates a pipeline from a mix of policy types. Accepted types: *CircuitBreaker, *Retry, *Bulkhead, *Timeout. Panics on unknown types.

func (*Pipeline) Execute

func (p *Pipeline) Execute(ctx context.Context, fn func(context.Context) error) error

Execute runs fn through all policies in order.

func (*Pipeline) Name

func (p *Pipeline) Name() string

Name implements Policy.

type Policy

type Policy interface {
	// Name returns a human-readable identifier used in metrics and logs.
	Name() string
}

Policy is the common interface for all resilience primitives. Policies are composable; a Pipeline implements Policy itself.

type Retry

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

Retry is a policy that re-executes a call on failure according to a configurable backoff and predicate.

func NewRetry

func NewRetry(cfg RetryConfig) *Retry

NewRetry creates a Retry policy with the given config.

func (*Retry) ExecuteRetry

func (r *Retry) ExecuteRetry(ctx context.Context, fn func(context.Context) error) error

ExecuteRetry runs fn with retry semantics. Returns the last error if all attempts are exhausted, wrapped in ErrMaxRetriesExceeded.

func (*Retry) Name

func (r *Retry) Name() string

Name implements Policy.

type RetryConfig

type RetryConfig struct {
	// Name identifies this policy in metrics.
	Name string

	// MaxAttempts is the total number of attempts (including the first).
	// A value of 3 means 1 initial call + 2 retries.
	// Default: 3.
	MaxAttempts int

	// Backoff controls how long to wait between attempts.
	// Default: ExponentialBackoff(100ms, 2.0, 10s).
	Backoff BackoffStrategy

	// RetryIf is called after each failure to decide whether to retry.
	// Default: RetryAll (retry any error).
	RetryIf RetryPredicate

	// Observer receives retry and failure events.
	Observer Observer

	// Clock is used for sleeping. Override in tests.
	Clock Clock
}

RetryConfig configures a Retry policy.

type RetryPredicate

type RetryPredicate func(err error, attempt int) bool

RetryPredicate decides whether a given error on a given attempt is retryable.

func RetryIf

func RetryIf(preds ...RetryPredicate) RetryPredicate

RetryIf composes multiple predicates with OR semantics.

func RetryOn

func RetryOn(target error) RetryPredicate

RetryOn returns a predicate that retries only when the error matches target (using errors.Is semantics).

type SlidingWindow

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

SlidingWindow tracks calls within a rolling time range divided into buckets. It is more accurate than CountWindow for high-variance traffic.

func NewSlidingWindow

func NewSlidingWindow(window time.Duration, numBuckets int) *SlidingWindow

NewSlidingWindow creates a window of the given duration split into `buckets` slots. Example: NewSlidingWindow(60*time.Second, 6) → 6×10s buckets, 60s total.

func (*SlidingWindow) FailureRate

func (w *SlidingWindow) FailureRate() float64

func (*SlidingWindow) Record

func (w *SlidingWindow) Record(success bool)

func (*SlidingWindow) Reset

func (w *SlidingWindow) Reset()

func (*SlidingWindow) SetClock

func (w *SlidingWindow) SetClock(clock Clock)

SetClock allows injecting a fake clock in tests to control time for sliding window boundaries.

func (*SlidingWindow) Total

func (w *SlidingWindow) Total() int64

type State

type State int

State represents the circuit breaker state.

const (
	StateClosed   State = iota // normal operation — calls pass through
	StateOpen                  // tripped — calls are rejected immediately
	StateHalfOpen              // recovering — a limited number of probe calls are allowed
)

func (State) String

func (s State) String() string

type Timeout

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

Timeout wraps a function with a deadline. If fn does not complete within the configured duration, the context passed to fn is cancelled and ErrTimeout is returned to the caller.

Importantly, Timeout waits for fn to return even after the deadline passes. This prevents goroutine leaks when fn does not respect context cancellation, though it will add latency in that case. Well-behaved fns should honour ctx.

func NewTimeout

func NewTimeout(cfg TimeoutConfig) *Timeout

NewTimeout creates a Timeout policy.

func (*Timeout) ExecuteTimeout

func (t *Timeout) ExecuteTimeout(ctx context.Context, fn func(context.Context) error) error

ExecuteTimeout runs fn with a context that expires after the configured duration.

func (*Timeout) Name

func (t *Timeout) Name() string

Name implements Policy.

type TimeoutConfig

type TimeoutConfig struct {
	// Name identifies this policy in metrics.
	Name string

	// Duration is the maximum time a call may take.
	// If the call exceeds this, it receives a cancelled context and
	// the caller receives ErrTimeout.
	Duration time.Duration

	// Observer receives success and failure events.
	Observer Observer

	// Clock is used for tracking. Override in tests.
	Clock Clock
}

TimeoutConfig configures a Timeout policy.

Directories

Path Synopsis
examples
basic command

Jump to

Keyboard shortcuts

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