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 ¶
- Variables
- func MakeCircuitBreakerErrorWithName(name string) func(time.Time) error
- func MakeSingletonBreaker[HKTT any](cb State[Pair[IORef[BreakerState], HKTT], HKTT], closedState ClosedState) func(HKTT) HKTT
- type BreakerState
- type CircuitBreakerError
- type ClosedState
- type Either
- type Endomorphism
- type IO
- type IORef
- type Metrics
- type Option
- type Ord
- type Pair
- type Predicate
- type Reader
- type ReaderIO
- type State
- type Void
Constants ¶
This section is empty.
Variables ¶
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] )
var AnyError = option.FromPredicate(E.IsNonNil)
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() }),
)
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
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
Pair is a type alias for pair.Pair, representing a tuple of two values. Used for grouping related values together.
type Predicate ¶
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 ¶
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 State ¶
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