Documentation ¶
Overview ¶
The breaker package provides circuit breaker primitives to enable one to safely interact with potentially-unreliable subsystems in a way that allows a graceful alternative behavior while in degraded state.
For instance, if your server depends on an external database and it becomes unavailable, your server can use a breaker to track these failures and provide an alternative workflow during this period of unavailability.
A circuit has two states:
CLOSED: The system decorated with this breaker is assumed to be available and the dependents thereof may use it freely.
OPEN: The system decorated with this breaker is assumed to be unavailable and the dependents thereof should not use it at this time.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Consecutive ¶
type Consecutive struct { // RetryTimeout is added to the time when the circuit first opens to determine // the next time the decorating operation will be allowed to occur. RetryTimeout time.Duration // FailureAllowance is the maximum consecutive failures that may occur before // the circuit will open. FailureAllowance uint // contains filtered or unexported fields }
Consecutive provides a simple circuit breaker with a deadline expiration strategy to determine when to reclose itself. Its initial state is CLOSED and will only open once a threshold of consecutive failures is reached.
It is concurrency safe.
Example ¶
package main import ( breaker "." "fmt" "time" ) // RemoteService is a fictitious interface to an encapsulation of some // unreliable subsystem that is outside of our domain of control. It should // be treated for this example as merely a recording of behavior. type RemoteService struct { // The subsystem's circuit breaker. Breaker breaker.Consecutive // The pre-recorded success or failure results that are used to drive // the behavior of this example. Results []bool } // ConductRequest is the supposed interface point for user's of this subsystem. // They call it as necessary to perform whatever work to yield the result they // want. func (s *RemoteService) ConductRequest() { // For purposes of not convoluting the example, we use real sleep operations // here. time.Sleep(time.Second/2 + time.Second/4) // If the circuit is broken, merely bail. if s.Breaker.Open() { fmt.Println("Unavailable; Trying Again Later...") return } // Emulate the actual remote interface here that is supposedly unreliable. err := s.performRequest() // WARNING: We make an implicit assumption that any err value is retryable // and not a permanent error. if err != nil { fmt.Println("Operation Failed") s.Breaker.Fail() } else { fmt.Println("Operation Succeeded") s.Breaker.Succeed() } } // performRequest models an interaction with an unreliable external // system---e.g., a remote API server. func (s *RemoteService) performRequest() error { result := s.Results[0] s.Results = s.Results[1:] if !result { return fmt.Errorf("Temporary Unavailable") } return nil } func main() { subsystem := &RemoteService{ Breaker: breaker.Consecutive{ FailureAllowance: 2, RetryTimeout: time.Second, }, Results: []bool{ true, // Success. true, // Success. false, // One-off failure; do not trip circuit. true, // This success negates past failures. false, // String of contiguous failures to create open circuit. false, // Open. :-( false, // Open; however, we've timed out. true, // We have a success here. }, } subsystem.ConductRequest() subsystem.ConductRequest() subsystem.ConductRequest() subsystem.ConductRequest() subsystem.ConductRequest() subsystem.ConductRequest() subsystem.ConductRequest() subsystem.ConductRequest() subsystem.ConductRequest() }
Output: Operation Succeeded Operation Succeeded Operation Failed Operation Succeeded Operation Failed Operation Failed Unavailable; Trying Again Later... Operation Failed Operation Succeeded
func (*Consecutive) Fail ¶
func (b *Consecutive) Fail()
Fail marks the decorated subsystem as having an operation fail and may trigger its subsequent circuit opening.
This should only be called if you can divine that the underlying failure is or should be considered transient for the given domain of work. For instance, high latency, erroneous responses, unavailability count as so-called temporary failures. Things like invalid user queries count as permanent errors and would never make sense to be retried.
func (*Consecutive) Open ¶
func (b *Consecutive) Open() bool
Open indicates whether the circuit for this subsystem is presently open.
func (*Consecutive) Reset ¶
func (b *Consecutive) Reset()
Reset returns this circuit back to its default state: closed.
This should be called if you can divine that the underlying subsystem has become unavailable before the deadline threshold has been reached.
func (*Consecutive) String ¶
func (b *Consecutive) String() string
func (*Consecutive) Succeed ¶
func (b *Consecutive) Succeed()
Succeed marks the decorated subsystem as having an operation succeed and will its circuits closure if it's presently open.