circuitbreaker

package
v2.2.13 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

Documentation

Overview

Package circuitbreaker provides error types and utilities for circuit breaker implementations.

Package circuitbreaker provides metrics collection for circuit breaker state transitions and events.

Package circuitbreaker provides a functional implementation of the circuit breaker pattern. A circuit breaker prevents cascading failures by temporarily blocking requests to a failing service, allowing it time to recover before retrying.

Thread Safety

All data structures in this package are immutable except for IORef[BreakerState]. The IORef provides thread-safe mutable state through atomic operations.

Immutable types (safe for concurrent use):

  • BreakerState (Either[openState, ClosedState])
  • openState
  • ClosedState implementations (closedStateWithErrorCount, closedStateWithHistory)
  • All function types and readers

Mutable types (thread-safe through atomic operations):

  • IORef[BreakerState] - provides atomic read/write/modify operations

ClosedState implementations must be thread-safe. The recommended approach is to return new copies for all operations (Empty, AddError, AddSuccess, Check), which provides automatic thread safety through immutability.

Index

Constants

This section is empty.

Variables

View Source
var (

	// MakeClosedIORef creates an IORef containing a closed circuit breaker state.
	// It wraps the provided ClosedState in a Right (closed) BreakerState and creates
	// a mutable reference to it.
	//
	// Parameters:
	//   - closedState: The initial closed state configuration
	//
	// Returns:
	//   - An IO operation that creates an IORef[BreakerState] initialized to closed state
	//
	// Thread Safety: The returned IORef[BreakerState] is thread-safe. It uses atomic
	// operations for all read/write/modify operations. The BreakerState itself is immutable.
	MakeClosedIORef = F.Flow2(
		createClosedCircuit,
		ioref.MakeIORef,
	)

	// IsOpen checks if a BreakerState is in the open state.
	// Returns true if the circuit breaker is open (blocking requests), false otherwise.
	IsOpen = either.IsLeft[openState, ClosedState]

	// IsClosed checks if a BreakerState is in the closed state.
	// Returns true if the circuit breaker is closed (allowing requests), false otherwise.
	IsClosed = either.IsRight[openState, ClosedState]
)

AnyError converts an error to an Option, wrapping non-nil errors in Some and nil errors in None.

This variable provides a functional way to handle errors by converting them to Option types. It's particularly useful in functional programming contexts where you want to treat errors as optional values rather than using traditional error handling patterns.

Behavior:

  • If the error is non-nil, returns Some(error)
  • If the error is nil, returns None

Thread Safety: This function is pure and safe for concurrent use.

Example:

err := errors.New("something went wrong")
optErr := AnyError(err)  // Some(error)

var noErr error = nil
optNoErr := AnyError(noErr)  // None

// Using in functional pipelines
result := F.Pipe2(
    someOperation(),
    AnyError,
    O.Map(func(e error) string { return e.Error() }),
)
View Source
var InfrastructureError = option.FromPredicate(shouldOpenCircuit)

InfrastructureError is a predicate that converts errors to Options based on whether they should trigger circuit breaker opening.

This variable provides a functional way to filter errors that represent infrastructure failures (network issues, server errors, timeouts, etc.) from application-level errors (validation errors, business logic errors, client errors).

Behavior:

  • Returns Some(error) if the error should open the circuit (infrastructure failure)
  • Returns None if the error should not open the circuit (application error)

Thread Safety: This function is pure and safe for concurrent use.

Use this in circuit breaker configurations to determine which errors should count toward the failure threshold.

Example:

// In a circuit breaker configuration
breaker := MakeCircuitBreaker(
    ...,
    checkError: InfrastructureError,  // Only infrastructure errors open the circuit
    ...,
)

// HTTP 500 error - returns Some(error)
result := InfrastructureError(&FH.HttpError{...}) // Some(error)

// HTTP 404 error - returns None
result := InfrastructureError(&FH.HttpError{...}) // None
View Source
var MakeCircuitBreakerError = MakeCircuitBreakerErrorWithName("Generic Circuit Breaker")

MakeCircuitBreakerError creates a new CircuitBreakerError with the specified reset time.

This constructor function creates a circuit breaker error that indicates when the circuit breaker will transition from the open state to the half-open state, allowing test requests to determine if the underlying service has recovered.

Parameters:

  • resetTime: The time at which the circuit breaker will attempt to close

Returns:

  • An error representing the circuit breaker open state

Thread Safety: This function is safe for concurrent use as it creates new error instances on each call.

Example:

resetTime := time.Now().Add(30 * time.Second)
err := MakeCircuitBreakerError(resetTime)
if cbErr, ok := err.(*CircuitBreakerError); ok {
    fmt.Printf("Circuit breaker will reset at: %s\n", cbErr.ResetAt)
}

Functions

func MakeCircuitBreakerErrorWithName

func MakeCircuitBreakerErrorWithName(name string) func(time.Time) error

MakeCircuitBreakerErrorWithName creates a circuit breaker error constructor with a custom name.

This function returns a constructor that creates CircuitBreakerError instances with a specific circuit breaker name. This is useful when you have multiple circuit breakers in your system and want to identify which one is open in error messages.

Parameters:

  • name: The name to identify this circuit breaker in error messages

Returns:

  • A function that takes a reset time and returns a CircuitBreakerError with the specified name

Thread Safety: The returned function is safe for concurrent use as it creates new error instances on each call.

Example:

makeDBError := MakeCircuitBreakerErrorWithName("Database Circuit Breaker")
err := makeDBError(time.Now().Add(30 * time.Second))
fmt.Println(err.Error())
// Output: circuit breaker is open [Database Circuit Breaker], will close at 2026-01-09 12:20:47.123 +0100 CET

func MakeSingletonBreaker

func MakeSingletonBreaker[HKTT any](
	cb State[Pair[IORef[BreakerState], HKTT], HKTT],
	closedState ClosedState,
) func(HKTT) HKTT

MakeSingletonBreaker creates a singleton circuit breaker operator for a higher-kinded type.

This function creates a circuit breaker that maintains its own internal state reference. It's called "singleton" because it creates a single, self-contained circuit breaker instance with its own IORef for state management. The returned function can be used to wrap computations with circuit breaker protection.

Type Parameters:

  • HKTT: The higher-kinded type representing the computation (e.g., IO[T], ReaderIO[R, T])

Parameters:

  • cb: The circuit breaker State monad created by MakeCircuitBreaker
  • closedState: The initial closed state configuration for the circuit breaker

Returns:

  • A function that wraps a computation (HKTT) with circuit breaker logic. The circuit breaker state is managed internally and persists across invocations.

Thread Safety: The returned function is thread-safe. The internal IORef[BreakerState] uses atomic operations to manage state. Multiple concurrent calls to the returned function will be properly serialized at the state modification level.

Example Usage:

// Create a circuit breaker for IO operations
breaker := MakeSingletonBreaker(
    MakeCircuitBreaker(...),
    MakeClosedStateCounter(3),
)

// Use it to wrap operations
protectedOp := breaker(myIOOperation)

Types

type BreakerState

type BreakerState = Either[openState, ClosedState]

BreakerState represents the current state of the circuit breaker. It is an Either type where:

  • Left[openState] represents an open circuit (requests are blocked)
  • Right[ClosedState] represents a closed circuit (requests are allowed through)

State Transitions:

  • Closed -> Open: When failure threshold is exceeded in ClosedState
  • Open -> Half-Open: When resetAt is reached (canaryRequest = true)
  • Half-Open -> Closed: When canary request succeeds
  • Half-Open -> Open: When canary request fails (with extended resetAt)

type CircuitBreakerError

type CircuitBreakerError struct {
	// Name: The name identifying this circuit breaker instance
	Name string

	// ResetAt: The time at which the circuit breaker will transition from open to half-open state
	ResetAt time.Time
}

CircuitBreakerError represents an error that occurs when a circuit breaker is in the open state.

When a circuit breaker opens due to too many failures, it prevents further operations from executing until a reset time is reached. This error type communicates that state and provides information about when the circuit breaker will attempt to close again.

Fields:

  • Name: The name identifying this circuit breaker instance
  • ResetAt: The time at which the circuit breaker will transition from open to half-open state

Thread Safety: This type is immutable and safe for concurrent use.

func (*CircuitBreakerError) Error

func (e *CircuitBreakerError) Error() string

Error implements the error interface for CircuitBreakerError.

Returns a formatted error message indicating that the circuit breaker is open and when it will attempt to close.

Returns:

  • A string describing the circuit breaker state and reset time

Thread Safety: This method is safe for concurrent use as it only reads immutable fields.

Example:

err := &CircuitBreakerError{Name: "API", ResetAt: time.Now().Add(30 * time.Second)}
fmt.Println(err.Error())
// Output: circuit breaker is open [API], will close at 2026-01-09 12:20:47.123 +0100 CET

type ClosedState

type ClosedState interface {
	// Empty returns a new ClosedState with all tracked failures cleared.
	// This is used when transitioning back to a closed state from an open state.
	//
	// Thread Safety: Returns a new instance; safe for concurrent use.
	Empty() ClosedState

	// AddError records a failure at the given time.
	// Returns an updated ClosedState reflecting the recorded failure.
	//
	// Thread Safety: Returns a new instance; safe for concurrent use.
	// The original ClosedState is not modified.
	AddError(time.Time) ClosedState

	// AddSuccess records a successful request at the given time.
	// Returns an updated ClosedState reflecting the successful request.
	//
	// Thread Safety: Returns a new instance; safe for concurrent use.
	// The original ClosedState is not modified.
	AddSuccess(time.Time) ClosedState

	// Check verifies if the circuit breaker should remain closed at the given time.
	// Returns Some(ClosedState) if the circuit should stay closed,
	// or None if the circuit should open due to exceeding the failure threshold.
	//
	// Thread Safety: Does not modify the receiver; safe for concurrent use.
	Check(time.Time) Option[ClosedState]
}

ClosedState represents the closed state of a circuit breaker. In the closed state, requests are allowed to pass through, but failures are tracked. If a failure condition is met, the circuit breaker transitions to an open state.

Thread Safety

All ClosedState implementations MUST be thread-safe. The recommended approach is to make all methods return new copies rather than modifying the receiver, which provides automatic thread safety through immutability.

Implementations should ensure that:

  • Empty() returns a new instance with cleared state
  • AddError() returns a new instance with the error recorded
  • AddSuccess() returns a new instance with success recorded
  • Check() does not modify the receiver

Both provided implementations (closedStateWithErrorCount and closedStateWithHistory) follow this pattern and are safe for concurrent use.

func MakeClosedStateCounter

func MakeClosedStateCounter(maxFailures uint) ClosedState

MakeClosedStateCounter creates a counter-based ClosedState implementation. The circuit breaker will open when the number of consecutive failures reaches maxFailures.

Parameters:

  • maxFailures: The threshold for consecutive failures. The circuit opens when failureCount >= maxFailures (greater than or equal to).

Returns:

  • A ClosedState that tracks failures using a simple counter.

Example:

  • If maxFailures is 3, the circuit will open on the 3rd consecutive failure.
  • Each AddError call increments the counter.
  • Each AddSuccess call resets the counter to 0 (only consecutive failures count).
  • Empty resets the counter to 0.

Behavior:

  • Check returns Some(ClosedState) when failureCount < maxFailures (circuit stays closed)
  • Check returns None when failureCount >= maxFailures (circuit should open)
  • AddSuccess resets the failure count, so only consecutive failures trigger circuit opening

Thread Safety: The returned ClosedState is safe for concurrent use. All methods return new instances rather than modifying the receiver.

func MakeClosedStateHistory

func MakeClosedStateHistory(
	timeWindow time.Duration,
	maxFailures uint) ClosedState

MakeClosedStateHistory creates a time-window-based ClosedState implementation. The circuit breaker will open when the number of failures within a sliding time window reaches maxFailures.

Unlike MakeClosedStateCounter which tracks consecutive failures, this implementation tracks all failures within a time window. However, any successful request will purge the entire history, effectively resetting the failure tracking.

Parameters:

  • timeWindow: The duration of the sliding time window. Failures older than this are automatically discarded from the history when new failures are added.
  • maxFailures: The threshold for failures within the time window. The circuit opens when the number of failures in the window reaches this value (failureCount >= maxFailures).

Returns:

  • A ClosedState that tracks failures using a time-based sliding window.

Example:

  • If timeWindow is 1 minute and maxFailures is 5, the circuit will open when 5 failures occur within any 1-minute period.
  • Failures older than 1 minute are automatically removed from the history when AddError is called.
  • Any successful request immediately purges all tracked failures from the history.

Behavior:

  • AddError records the failure timestamp and removes failures outside the time window (older than currentTime - timeWindow).
  • AddSuccess purges the entire failure history (all tracked failures are removed).
  • Check returns Some(ClosedState) when failureCount < maxFailures (circuit stays closed).
  • Check returns None when failureCount >= maxFailures (circuit should open).
  • Empty purges the entire failure history.

Time Window Management:

  • The history is automatically pruned on each AddError call to remove failures older than currentTime - timeWindow.
  • The history is kept sorted by time for efficient binary search and pruning.

Important Note:

  • A successful request resets everything by purging the entire history. This means that unlike a pure sliding window, a single success will clear all tracked failures, even those within the time window. This behavior is similar to MakeClosedStateCounter but with time-based tracking for failures.

Thread Safety: The returned ClosedState is safe for concurrent use. All methods return new instances with new slices rather than modifying the receiver. The history slice is never modified in place.

Use Cases:

  • Systems where a successful request indicates recovery and past failures should be forgotten.
  • Rate limiting with success-based reset: Allow bursts of failures but reset on success.
  • Hybrid approach: Time-based failure tracking with success-based recovery.

type Either

type Either[E, A any] = either.Either[E, A]

Either is a type alias for either.Either, representing a value that can be one of two types. Left[E] represents an error or alternative path, Right[A] represents the success path.

type Endomorphism

type Endomorphism[A any] = endomorphism.Endomorphism[A]

Endomorphism is a type alias for endomorphism.Endomorphism, representing a function from A to A. Used for transformations that preserve the type.

type IO

type IO[T any] = io.IO[T]

IO is a type alias for io.IO, representing a lazy computation that produces a value of type T. Used for side-effectful operations that are deferred until execution.

type IORef

type IORef[T any] = ioref.IORef[T]

IORef is a type alias for ioref.IORef, representing a mutable reference to a value of type T. Used for managing mutable state in a functional way with IO operations.

type Metrics

type Metrics interface {
	// Accept records that a request was accepted and allowed through the circuit breaker.
	// This is called when the circuit is closed or in half-open state (canary request).
	//
	// Parameters:
	//   - time.Time: The timestamp when the request was accepted
	//
	// Returns:
	//   - IO[Void]: An IO operation that records the acceptance when executed
	//
	// Thread Safety: Must be safe to call concurrently.
	Accept(time.Time) IO[Void]

	// Reject records that a request was rejected because the circuit breaker is open.
	// This is called when a request is blocked due to the circuit being in open state
	// and the reset time has not been reached.
	//
	// Parameters:
	//   - time.Time: The timestamp when the request was rejected
	//
	// Returns:
	//   - IO[Void]: An IO operation that records the rejection when executed
	//
	// Thread Safety: Must be safe to call concurrently.
	Reject(time.Time) IO[Void]

	// Open records that the circuit breaker transitioned to the open state.
	// This is called when the failure threshold is exceeded and the circuit opens
	// to prevent further requests from reaching the failing service.
	//
	// Parameters:
	//   - time.Time: The timestamp when the circuit opened
	//
	// Returns:
	//   - IO[Void]: An IO operation that records the state transition when executed
	//
	// Thread Safety: Must be safe to call concurrently.
	Open(time.Time) IO[Void]

	// Close records that the circuit breaker transitioned to the closed state.
	// This is called when:
	//   - A canary request succeeds in half-open state
	//   - The circuit is manually reset
	//   - The circuit breaker is initialized
	//
	// Parameters:
	//   - time.Time: The timestamp when the circuit closed
	//
	// Returns:
	//   - IO[Void]: An IO operation that records the state transition when executed
	//
	// Thread Safety: Must be safe to call concurrently.
	Close(time.Time) IO[Void]

	// Canary records that a canary (test) request is being attempted.
	// This is called when the circuit is in half-open state and a single test request
	// is allowed through to check if the service has recovered.
	//
	// Parameters:
	//   - time.Time: The timestamp when the canary request was initiated
	//
	// Returns:
	//   - IO[Void]: An IO operation that records the canary attempt when executed
	//
	// Thread Safety: Must be safe to call concurrently.
	Canary(time.Time) IO[Void]
}

Metrics defines the interface for collecting circuit breaker metrics and events. Implementations can use this interface to track circuit breaker behavior for monitoring, alerting, and debugging purposes.

All methods accept a time.Time parameter representing when the event occurred, and return an IO[Void] operation that performs the metric recording when executed.

Thread Safety: Implementations must be thread-safe as circuit breakers may be accessed concurrently from multiple goroutines.

Example Usage:

logger := log.New(os.Stdout, "[CircuitBreaker] ", log.LstdFlags)
metrics := MakeMetricsFromLogger("API-Service", logger)

// In circuit breaker implementation
io.Run(metrics.Accept(time.Now()))  // Record accepted request
io.Run(metrics.Reject(time.Now()))  // Record rejected request
io.Run(metrics.Open(time.Now()))    // Record circuit opening
io.Run(metrics.Close(time.Now()))   // Record circuit closing
io.Run(metrics.Canary(time.Now()))  // Record canary request

func MakeMetricsFromLogger

func MakeMetricsFromLogger(name string, logger *log.Logger) Metrics

MakeMetricsFromLogger creates a Metrics implementation that logs circuit breaker events using the provided log.Logger.

This is a simple metrics implementation suitable for development, debugging, and basic production monitoring. For more sophisticated metrics collection (e.g., Prometheus, StatsD), implement the Metrics interface with a custom type.

Parameters:

  • name: A human-readable name identifying the circuit breaker instance. This name appears in all log messages to distinguish between multiple circuit breakers.
  • logger: The log.Logger instance to use for writing log messages. If nil, this will panic when metrics are recorded.

Returns:

  • Metrics: A thread-safe Metrics implementation that logs events

Thread Safety: The returned Metrics implementation is safe for concurrent use as log.Logger is thread-safe.

Example:

logger := log.New(os.Stdout, "[CB] ", log.LstdFlags)
metrics := MakeMetricsFromLogger("UserService", logger)

// Use with circuit breaker
io.Run(metrics.Open(time.Now()))
// Output: [CB] 2026/01/09 15:30:45 Open: UserService, 2026-01-09 15:30:45.123 +0100 CET

io.Run(metrics.Reject(time.Now()))
// Output: [CB] 2026/01/09 15:30:46 Reject: UserService, 2026-01-09 15:30:46.456 +0100 CET

func MakeVoidMetrics added in v2.1.9

func MakeVoidMetrics() Metrics

MakeVoidMetrics creates a no-op Metrics implementation that performs no operations. All methods return the same pre-allocated IO[Void] operation that does nothing when executed.

This is useful for:

  • Testing scenarios where metrics collection is not needed
  • Production environments where metrics overhead should be eliminated
  • Benchmarking circuit breaker logic without metrics interference
  • Default initialization when no metrics implementation is provided

Returns:

  • Metrics: A thread-safe no-op Metrics implementation

Thread Safety: The returned Metrics implementation is safe for concurrent use. All methods return the same immutable IO[Void] operation.

Performance: This is the most efficient Metrics implementation with minimal overhead. The IO[Void] operation is pre-allocated once and reused for all method calls.

Example:

metrics := MakeVoidMetrics()

// All operations do nothing
io.Run(metrics.Open(time.Now()))    // No-op
io.Run(metrics.Accept(time.Now()))  // No-op
io.Run(metrics.Reject(time.Now()))  // No-op

// Useful for testing
breaker := MakeCircuitBreaker(
    // ... other parameters ...
    MakeVoidMetrics(), // No metrics overhead
)

type Option

type Option[A any] = option.Option[A]

Option is a type alias for option.Option, representing an optional value. It can be either Some(value) or None, used for safe handling of nullable values.

type Ord

type Ord[A any] = ord.Ord[A]

Ord is a type alias for ord.Ord, representing a total ordering on type A. Used for comparing values in a consistent way.

type Pair

type Pair[L, R any] = pair.Pair[L, R]

Pair is a type alias for pair.Pair, representing a tuple of two values. Used for grouping related values together.

type Predicate

type Predicate[A any] = predicate.Predicate[A]

Predicate is a type alias for predicate.Predicate, representing a function that tests a value. Returns true if the value satisfies the predicate condition, false otherwise.

type Reader

type Reader[R, A any] = reader.Reader[R, A]

Reader is a type alias for reader.Reader, representing a computation that depends on an environment R and produces a value of type A. Used for dependency injection and configuration.

type ReaderIO added in v2.1.9

type ReaderIO[R, A any] = readerio.ReaderIO[R, A]

type State

type State[T, R any] = state.State[T, R]

State is a type alias for state.State, representing a stateful computation. It transforms a state of type T and produces a result of type R.

func MakeCircuitBreaker

func MakeCircuitBreaker[E, T, HKTT, HKTOP, HKTHKTT any](

	left func(E) HKTT,
	chainFirstIOK func(io.Kleisli[T, BreakerState]) func(HKTT) HKTT,
	chainFirstLeftIOK func(io.Kleisli[E, BreakerState]) func(HKTT) HKTT,

	chainFirstIOK2 func(io.Kleisli[Either[E, T], Void]) func(HKTT) HKTT,

	fromIO func(IO[func(HKTT) HKTT]) HKTOP,
	flap func(HKTT) func(HKTOP) HKTHKTT,
	flatten func(HKTHKTT) HKTT,

	currentTime IO[time.Time],
	closedState ClosedState,
	makeError Reader[time.Time, E],
	checkError option.Kleisli[E, E],
	policy retry.RetryPolicy,
	metrics Metrics,
) State[Pair[IORef[BreakerState], HKTT], HKTT]

MakeCircuitBreaker creates a circuit breaker implementation for a higher-kinded type.

This is a generic circuit breaker factory that works with any monad-like type (HKTT). It implements the circuit breaker pattern by wrapping operations and managing state transitions between closed, open, and half-open states based on failure rates and retry policies.

Type Parameters:

  • E: The error type
  • T: The success value type
  • HKTT: The higher-kinded type representing the computation (e.g., IO[T], ReaderIO[R, T])
  • HKTOP: The higher-kinded type for operators (e.g., IO[func(HKTT) HKTT])
  • HKTHKTT: The nested higher-kinded type (e.g., IO[IO[T]])

Parameters:

  • left: Constructs an error result in HKTT from an error value
  • chainFirstIOK: Chains an IO operation that runs after success, preserving the original value
  • chainFirstLeftIOK: Chains an IO operation that runs after error, preserving the original error
  • fromIO: Lifts an IO operation into HKTOP
  • flap: Applies a value to a function wrapped in a higher-kinded type
  • flatten: Flattens nested higher-kinded types (join operation)
  • currentTime: IO operation that provides the current time
  • closedState: The initial closed state configuration
  • makeError: Creates an error from a reset time when the circuit is open
  • checkError: Predicate to determine if an error should trigger circuit breaker logic
  • policy: Retry policy for determining reset times when circuit opens
  • logger: Logging function for circuit breaker events

Thread Safety: The returned State monad creates operations that are thread-safe when executed. The IORef[BreakerState] uses atomic operations for all state modifications. Multiple concurrent requests will be properly serialized at the IORef level.

Returns:

  • A State monad that transforms a pair of (IORef[BreakerState], HKTT) into HKTT, applying circuit breaker logic to the computation

type Void

type Void = function.Void

Jump to

Keyboard shortcuts

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