Documentation
¶
Overview ¶
Package circuitry provides a framework for building distribuited Circuit Breakers. It's primarily designed to be able to synchronize state with a backend so that a distributed system can have one Circuit Breaker for any piece it cares to implement that for.
For example, in a system calling any number of other systems, it can be beneficial to the health of the first system to implement a circuit breaker around calls to the other dependencies they have. If you have N dependencies on other systems, you could have N circuit breakers that would work across all of the instances of your system to protect it as a whole. If instead you had in memory only circuit breakers, each instance of that system would have to individually reach your failure threshold to trip the breaker before all instances have stopped talking to it. That could affect availability and performance for your users. Alternatively, you might have ot set the threshold artificially low and risk the breaker opening when it didn't need to.
circuitry aims to provide an excellent distributed circuit breaker pattern as well as supportive libraries and a handful of curated backends. For example, circuitry provides interfaces for structured logging libraries as well as implementations for logrus and slog. Likewise, it allows for metrics to be gathered from it by providing interfaces for users to specify their sink. Finally, it has a backend interface and provides implementations in Redis and DynamoDB for production usage and for reference.
Usage ¶
The main way to interact with circuitry is to create a CircuitBreakerFactory. This looks like:
import (
"log/slog"
"time"
"github.com/sigmavirus24/circuitry"
)
logger := slog.Default()
settings, err := circuitry.NewFactorySettings(
circuitry.WithDefaultTripFunc(),
circuitry.WithDefaultNameFunc(),
circuitry.WithFailureCountThreshold(15),
circuitry.WithAllowAfter(15 * time.Minute),
circuitry.WithCyclicClearAfter(1 * time.Hour),
)
if err != nil {
logger.With("err", err).Error("could not instantiate factory settings")
}
factory := circuitry.NewCircuitBreakerFactory(settings)
Once you have a factory you can create any number of named CircuitBreakers. circuitry also allows you to include relevant context when creating a CircuitBreaker. For example, maybe you have tracing context you
Additional Resources ¶
For additional information see also: - https://learn.microsoft.com/en-us/previous-versions/msp-n-p/dn589784(v=pandp.10)?redirectedfrom=MSDN - https://www.redhat.com/architect/circuit-breaker-architecture-pattern - https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/circuit-breaker.html
Index ¶
- Constants
- Variables
- func DefaultNameFunc(circuit string, circuitContext map[string]any) string
- func DefaultTripFunc(_ string, configuredThreshold uint64, information CircuitInformation) bool
- func WrapExpectedConditionError(err error) error
- type CircuitBreaker
- type CircuitBreakerFactory
- type CircuitInformation
- type CircuitSpecificSettingsConflictError
- type CircuitState
- type ExecutionStatus
- type ExpectedConditionError
- type ExpectedErrorMatcherFunc
- type FactorySettings
- type IsExpectedErrorer
- type NameFunc
- type SettingsConflictError
- type SettingsOption
- func WithAllowAfter(duration time.Duration) SettingsOption
- func WithCircuitSpecificErrorMatcher(circuitName string, matcher ExpectedErrorMatcherFunc) SettingsOption
- func WithCloseThreshold(threshold uint64) SettingsOption
- func WithCyclicClearAfter(duration time.Duration) SettingsOption
- func WithDefaultFallbackErrorMatcher() SettingsOption
- func WithDefaultNameFunc() SettingsOption
- func WithDefaultTripFunc() SettingsOption
- func WithFailureCountThreshold(threshold uint64) SettingsOption
- func WithFallbackErrorMatcher(matcher ExpectedErrorMatcherFunc) SettingsOption
- func WithNameFunc(namefn NameFunc) SettingsOption
- func WithStateChangeCallback(cb StateChangeFunc) SettingsOption
- func WithStorageBackend(backend StorageBackender) SettingsOption
- func WithTripFunc(tf WillTripFunc) SettingsOption
- type StateChangeFunc
- type StorageBackender
- type WillTripFunc
- type WorkFn
Constants ¶
const ( // ErrSettingConflict is returned when a setting has already been set and // the function refuses to override it ErrSettingConflict = constError("setting has already been configured via an option function, refusing to override") // ErrCircuitBreakerAlreadyStarted is returned when a CircuitBreaker has // already been started ErrCircuitBreakerAlreadyStarted = constError("circuit breaker has already been started and is executing") // ErrCircuitBreakerOpen is returned when a CircuitBreaker has already // been tripped and is in the open state ErrCircuitBreakerOpen = constError("circuit breaker is open") // ErrTooManyRequests is returned when a CircuitBreaker is in the // CircuitHalfOpen state and too many requests have been made ErrTooManyRequests = constError("too many requests with a circuit breaker in the half-open state") // ErrProvisioningStorageBackend is returned when a StorageBackend // encounters an issue during it's creation that is not a setting conflict ErrProvisioningStorageBackend = constError("could not provision storage backend") )
Variables ¶
var ( // ErrStorageBackendAlreadySet is returned when the StorageBackend setting // has alredy been configured ErrStorageBackendAlreadySet = newSettingsConflictError("StorageBackend") // ErrFallbackErrorMatcherAlreadySet is returned when the ErrorMatcher setting has // already been configured ErrFallbackErrorMatcherAlreadySet = newSettingsConflictError("FallbackErrorMatcher") // ErrNameFnAlreadySet is returned when the NameFn setting has already been // configured ErrNameFnAlreadySet = newSettingsConflictError("NameFn") // ErrStateChangeCallbackAlreadySet is returned when the // StateChangeCallback setting has already been configured ErrStateChangeCallbackAlreadySet = newSettingsConflictError("StateChangeCallback") // ErrFailureCountThresholdAlreadySet is returned when the // FailureCountThreshold setting has already been configured ErrFailureCountThresholdAlreadySet = newSettingsConflictError("FailureCountThreshold") // ErrCloseThresholdAlreadySet is returned when the // CloseThreshold setting has already been configured ErrCloseThresholdAlreadySet = newSettingsConflictError("CloseThreshold") // ErrWillTripCircuitAlreadySet is returned when the WillTripCircuit // setting has already been configured ErrWillTripCircuitAlreadySet = newSettingsConflictError("WillTripCircuit") )
Functions ¶
func DefaultNameFunc ¶
DefaultNameFunc is a default name implementation that simply uses the name of te circuit for the name of a given circuit breaker
func DefaultTripFunc ¶
func DefaultTripFunc(_ string, configuredThreshold uint64, information CircuitInformation) bool
DefaultTripFunc is the default function used to determine if a CircuitBreaker is ready to trip. By default this returns true if the number of ConsecutiveFailures is greater than the FailureCountThreshold
func WrapExpectedConditionError ¶
WrapExpectedConditionError wraps an error with the ExpectedConditionError type to signal that the error should not cause a failure.
Types ¶
type CircuitBreaker ¶
type CircuitBreaker interface {
// Start attempts to start work protected by the CircuitBreaker
Start(context.Context) error
// Name returns the name of the CircuitBreaker in use
Name() string
// Execute takes a function that does the work to be protected by the
// CircuitBreaker and manages calling Start and End around the work
// function for you. It returns the result of the function, the error if
// returned by the function, and the error if returned from storing the
// state in the backend. The third value may also be an error if the
// CircuitBreaker is Open.
Execute(context.Context, WorkFn) (workResult any, workErr error, circuitErr error)
// End updates the status of the CircuitBreaker and returns an error if
// there is an issue updating the storage backend
End(context.Context, error) error
// State returns the current state of the CircuitBreaker
State(context.Context) (CircuitState, error)
// Information returns the current CircuitInformation representing the
// state of the CircuitBreaker
Information(context.Context) (CircuitInformation, error)
}
CircuitBreaker defines the interface for an implementation of a circuit breaker.
type CircuitBreakerFactory ¶
type CircuitBreakerFactory struct {
// contains filtered or unexported fields
}
CircuitBreakerFactory creates CircuitBreakers for a given named circuit
func NewCircuitBreakerFactory ¶
func NewCircuitBreakerFactory(s *FactorySettings) *CircuitBreakerFactory
NewCircuitBreakerFactory builds a new CircuitBreakerFactory from FactorySettings supplied
func (*CircuitBreakerFactory) BreakerFor ¶
func (cbf *CircuitBreakerFactory) BreakerFor(name string, circuitContext map[string]any) CircuitBreaker
BreakerFor builds a new CircuitBreaker for the given named circuit and includes the circuit breaker contxt provided. The context is passed into the naming function and can be used by custom naming functions to produce names based off of a template
type CircuitInformation ¶
type CircuitInformation struct {
State CircuitState `json:"state"`
Generation uint64 `json:"generation"`
ConsecutiveFailures uint64 `json:"consecutive_failures"`
ConsecutiveSuccesses uint64 `json:"consecutive_successes"`
Total uint64 `json:"total"`
TotalFailures uint64 `json:"total_failures"`
TotalSuccesses uint64 `json:"total_successes"`
ExpiresAfter time.Time `json:"expires_after"`
}
CircuitInformation describes the full state of a given CircuitBreaker to be for use with a StorageBackender
func NewCircuitInformation ¶
func NewCircuitInformation(expiredAfter time.Duration) CircuitInformation
NewCircuitInformation creates a new CircuitInformation with the state being Open and an expiration
type CircuitSpecificSettingsConflictError ¶
type CircuitSpecificSettingsConflictError struct {
SettingsConflictError
CircuitName string
}
CircuitSpecificSettingsConflictError represents a conflict for a FactorySettings for something that is specific to a given CircuitBreaker
func (CircuitSpecificSettingsConflictError) Error ¶
func (e CircuitSpecificSettingsConflictError) Error() string
func (CircuitSpecificSettingsConflictError) Unwrap ¶
func (e CircuitSpecificSettingsConflictError) Unwrap() error
type CircuitState ¶
type CircuitState uint32
CircuitState describes the states of a given CircuitBreaker
const ( // CircuitClosed describes a closed CircuitBreaker CircuitClosed CircuitState = iota // CircuitOpen describes an open CircuitBreaker CircuitOpen // CircuitHalfOpen describes a half-open CircuitBreaker CircuitHalfOpen )
func (CircuitState) String ¶
func (cs CircuitState) String() string
type ExecutionStatus ¶
type ExecutionStatus uint32
ExecutionStatus describes the status of a given execution
const ( // ExecutionSucceeded describes a successful execution ExecutionSucceeded ExecutionStatus = iota // ExecutionFailed describes a failed execution ExecutionFailed )
func DefaultErrorMatcher ¶
func DefaultErrorMatcher(err error) ExecutionStatus
DefaultErrorMatcher is a default implementation of ExpectedErrorMatcher and assumes all errors are failed executions
func (ExecutionStatus) String ¶
func (es ExecutionStatus) String() string
type ExpectedConditionError ¶
type ExpectedConditionError struct {
// contains filtered or unexported fields
}
ExpectedConditionError is provided so users can intentionally wrap an error they do not want to count as a failure without having to create additional error matchers
func (*ExpectedConditionError) Error ¶
func (e *ExpectedConditionError) Error() string
func (*ExpectedConditionError) IsExpected ¶
func (e *ExpectedConditionError) IsExpected() bool
IsExpected indicates that this is an expected error condition
func (*ExpectedConditionError) Unwrap ¶
func (e *ExpectedConditionError) Unwrap() error
type ExpectedErrorMatcherFunc ¶
type ExpectedErrorMatcherFunc func(err error) ExecutionStatus
ExpectedErrorMatcherFunc defines the signature of a function that allows the user to define when an error is expected and the execution should not be considered to be failed
type FactorySettings ¶
type FactorySettings struct {
StorageBackend StorageBackender
NameFn NameFunc
FallbackErrorMatcher ExpectedErrorMatcherFunc
CircuitSpecificErrorMatcher map[string]ExpectedErrorMatcherFunc
FailureCountThreshold uint64
CloseThreshold uint64
AllowAfter time.Duration
CyclicClearAfter time.Duration
StateChangeCallback StateChangeFunc
WillTripCircuit WillTripFunc
}
FactorySettings contains information for configuring a CircuitBreakerFactory and any CircuitBreaker it creates. The settings are:
- `StorageBackend` which allows for remote storage of circuit breaker state
- `NameFn` which allows the CircuitBreakerFactory to dynamically generate names
- `FallbackErrorMatcher` which provides for a default error matcher if one isn't found for a specific CircuitBreaker by name
- `CircuitSpecificErrorMatcher` which provides a way for different CircuitBreakers to have specific error matchers
- `FailureCountThreshold` defines the threshold after which a CircuitBreaker transitions to the CircuitOpen CircuitState
- `CloseThreshold` defines the number of requests after which a CircuitBreaker in the CircuitHalfOpen state returns to the CircuitClosed state.
- `AllowAfter` defines the time after which a CircuitBreaker in the `CircuitOpen` CircuitState transitions to the `CircuitHalfOpen` CircuitState. If not specified, the default is 60 seconds.
- `CyclicClearAfter` defines the time after which a CircuitBreaker resets its internal counts. If not specified, the CircuitBreaker never resets its internal counts.
- `StateChangeCallback` provides a way to learn when the CircuitBreaker state is changing.
- `WillTripCircuit` provides a way to customize whether the CircuitBreaker will trip beyond the failure counts configured
func NewFactorySettings ¶
func NewFactorySettings(opts ...SettingsOption) (*FactorySettings, error)
NewFactorySettings constructs a Setings struct with the provided options
func (*FactorySettings) GenerateName ¶
func (s *FactorySettings) GenerateName(circuit string, circuitContext map[string]any) string
GenerateName builds a name for a CircuitBreaker
type IsExpectedErrorer ¶
type IsExpectedErrorer interface {
IsExpected() bool
}
IsExpectedErrorer defines an interface that one can use when defining their own concrete error types. It allows users to add an IsExpected() method to their function to signal to cicuitry that the error is one that should not count as a failure.
type SettingsConflictError ¶
type SettingsConflictError struct {
FactorySettingsName string
// contains filtered or unexported fields
}
SettingsConflictError contains the FactorySettingsName in the error and provides an overarching type for all errors resulting from a conflict
func (SettingsConflictError) Error ¶
func (sce SettingsConflictError) Error() string
func (SettingsConflictError) Unwrap ¶
func (sce SettingsConflictError) Unwrap() error
type SettingsOption ¶
type SettingsOption func(s *FactorySettings) error
SettingsOption configures an option on a setting
func WithAllowAfter ¶
func WithAllowAfter(duration time.Duration) SettingsOption
WithAllowAfter configures the AllowAfter setting and always overrides it.
func WithCircuitSpecificErrorMatcher ¶
func WithCircuitSpecificErrorMatcher(circuitName string, matcher ExpectedErrorMatcherFunc) SettingsOption
WithCircuitSpecificErrorMatcher sets a specific error matcher for a given circuit with the value provided
func WithCloseThreshold ¶
func WithCloseThreshold(threshold uint64) SettingsOption
WithCloseThreshold configures the number of consecutive successful requests needed for a CircuitHalfOpen to transition to CircuitClosed
func WithCyclicClearAfter ¶
func WithCyclicClearAfter(duration time.Duration) SettingsOption
WithCyclicClearAfter configures the CyclicClearAfter setting and always overrides it.
func WithDefaultFallbackErrorMatcher ¶
func WithDefaultFallbackErrorMatcher() SettingsOption
WithDefaultFallbackErrorMatcher configures the ErrorMatcher setting a the default value, DefaultErrorMatcher
func WithDefaultNameFunc ¶
func WithDefaultNameFunc() SettingsOption
WithDefaultNameFunc configure the Name setting a the defualt value, DefaultNameFunc
func WithDefaultTripFunc ¶
func WithDefaultTripFunc() SettingsOption
WithDefaultTripFunc sets the WillTripCircuit setting to the default function `DefaultTripFunc`
func WithFailureCountThreshold ¶
func WithFailureCountThreshold(threshold uint64) SettingsOption
WithFailureCountThreshold configures the threshold where the circuit breaker trips to closed
func WithFallbackErrorMatcher ¶
func WithFallbackErrorMatcher(matcher ExpectedErrorMatcherFunc) SettingsOption
WithFallbackErrorMatcher configures the ErrorMatcher with the provided matcher
func WithNameFunc ¶
func WithNameFunc(namefn NameFunc) SettingsOption
WithNameFunc configures the Name setting with the provided NameFunc
func WithStateChangeCallback ¶
func WithStateChangeCallback(cb StateChangeFunc) SettingsOption
WithStateChangeCallback configures the StateChangeCallback setting with the provided StateChangeFunc if it's not already set
func WithStorageBackend ¶
func WithStorageBackend(backend StorageBackender) SettingsOption
WithStorageBackend allows the user to specify a StorageBackender implementation for CircuitBreakers
func WithTripFunc ¶
func WithTripFunc(tf WillTripFunc) SettingsOption
WithTripFunc configures the WillTripCircuit setting to have custom logic for when a CircuitBreaker trips open
type StateChangeFunc ¶
type StateChangeFunc func(name string, circuitContext map[string]any, from, to CircuitState)
StateChangeFunc defines the signature of a function that allows the user to define custome logic around the changing of CircuitBreaker state.
type StorageBackender ¶
type StorageBackender interface {
// Store the circuit information for the given string name
Store(context.Context, string, CircuitInformation) error
// Retrieve the circuit information
Retrieve(context.Context, string) (CircuitInformation, error)
// Lock provides a lock for the given name to allow for atomicity via the
// backend
Lock(context.Context, string) (sync.Locker, error)
}
StorageBackender defines the contract expected of a Storage Backend for the cicruit breaker in use.
type WillTripFunc ¶
type WillTripFunc func(name string, configuredThreshold uint64, information CircuitInformation) bool
WillTripFunc defines the signature of a function that allows the user to define when a CircuitBreaker may trip. It accepts the name of the CircuitBreaker, the value of the FailureCountThreshold from the FactorySetings, and the information about the state of the circuit. It returns `true` if the CircuitBreaker should transition to `CircuitOpen`