diff --git a/gomore/06_circuit_breaker/circuit_breaker.go b/gomore/06_circuit_breaker/circuit_breaker.go new file mode 100644 index 0000000..5bd7b4d --- /dev/null +++ b/gomore/06_circuit_breaker/circuit_breaker.go @@ -0,0 +1,99 @@ +package circuit + +import ( + "context" + "errors" + "time" +) + +//ErrServiceUnavailable for error +var ( + ErrServiceUnavailable = errors.New("Service Unavailable") + FailureThreshold = 10 +) + +//State of current switch +type State int + +//State of current switch +const ( + UnknownState State = iota + FailureState + SuccessState +) + +//Circuit of action stream +type Circuit func(context.Context) error + +//Counter interface +type Counter interface { + Count(State) + ConsecutiveFailures() uint32 + LastActivity() time.Time + Reset() +} + +type counters struct { + state State + lastActivity time.Time + counts uint32 //counts of failures +} + +func (c *counters) Count(State) { + +} + +func (c *counters) ConsecutiveFailures() uint32 { + + return 0 +} + +func (c *counters) LastActivity() time.Time { + return c.lastActivity +} + +func (c *counters) Reset() { + +} + +//NewCounter New Counter for Circuit Breaker +func NewCounter() Counter { + return &counters{} +} + +//Breaker of circuit +func Breaker(c Circuit, failureThreshold uint32) Circuit { + + cnt := NewCounter() + + return func(ctx context.Context) error { + if cnt.ConsecutiveFailures() >= failureThreshold { + + canRetry := func(cnt Counter) bool { + backoffLevel := cnt.ConsecutiveFailures() - failureThreshold + + // Calculates when should the circuit breaker resume propagating requests + // to the service + shouldRetryAt := cnt.LastActivity().Add(time.Second * 2 << backoffLevel) + + return time.Now().After(shouldRetryAt) + } + + if !canRetry(cnt) { + // Fails fast instead of propagating requests to the circuit since + // not enough time has passed since the last failure to retry + return ErrServiceUnavailable + } + } + + // Unless the failure threshold is exceeded the wrapped service mimics the + // old behavior and the difference in behavior is seen after consecutive failures + if err := c(ctx); err != nil { + cnt.Count(FailureState) + return err + } + + cnt.Count(SuccessState) + return nil + } +} diff --git a/gomore/06_circuit_breaker/circuit_breaker_test.go b/gomore/06_circuit_breaker/circuit_breaker_test.go index 9a53c5b..1bba66f 100644 --- a/gomore/06_circuit_breaker/circuit_breaker_test.go +++ b/gomore/06_circuit_breaker/circuit_breaker_test.go @@ -1,91 +1,9 @@ package circuit import ( - "context" - "errors" - "time" + "testing" ) -var ( - ErrServiceUnavailable = errors.New("Service Unavailable") -) - -type State int - -const ( - UnknownState State = iota - FailureState - SuccessState -) - -//Counter interface -type Counter interface { - Count(State) - ConsecutiveFailures() uint32 - LastActivity() time.Time - Reset() -} - -type counters struct { - state State - lastActivity time.Time -} - -func (c *counters) Count(State) { +func TestBasicBreaker(t *testing.T) { } - -func (c *counters) ConsecutiveFailures() uint32 { - - return 0 -} - -func (c *counters) LastActivity() time.Time { - return c.lastActivity -} - -func (c *counters) Reset() { - -} - -func NewCounter() Counter { - var i Counter - return i -} - -type Circuit func(context.Context) error - -func Breaker(c Circuit, failureThreshold uint32) Circuit { - - cnt := NewCounter() - - return func(ctx context.Context) error { - if cnt.ConsecutiveFailures() >= failureThreshold { - canRetry := func(cnt Counter) bool { - backoffLevel := cnt.ConsecutiveFailures() - failureThreshold - - // Calculates when should the circuit breaker resume propagating requests - // to the service - shouldRetryAt := cnt.LastActivity().Add(time.Second * 2 << backoffLevel) - - return time.Now().After(shouldRetryAt) - } - - if !canRetry(cnt) { - // Fails fast instead of propagating requests to the circuit since - // not enough time has passed since the last failure to retry - return ErrServiceUnavailable - } - } - - // Unless the failure threshold is exceeded the wrapped service mimics the - // old behavior and the difference in behavior is seen after consecutive failures - if err := c(ctx); err != nil { - cnt.Count(FailureState) - return err - } - - cnt.Count(SuccessState) - return nil - } -}