Skip to content

Commit

Permalink
Rename package from circuitbreaker to circuit
Browse files Browse the repository at this point in the history
Closes #19
  • Loading branch information
rubyist committed Aug 16, 2014
1 parent 9ae5292 commit c834a51
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 70 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog
All notable changes to this project will be documented in this file.

## 1.0.0 - 2014-08-16

### Added
- This will be the public API for version 1.0.0. This project will follow semver rules.
16 changes: 5 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ application makes a lot of these requests, many resources can be tied
up waiting for these time outs to occur. A circuit breaker wraps these
remote calls and will trip after a defined amount of failures or time outs
occur. When a circuit breaker is tripped any future calls will avoid making
the remote call and return an error to the client. In the meantime, the
the remote call and return an error to the caller. In the meantime, the
circuit breaker will periodically allow some calls to be tried again and
will close the circuit if those are successful.

Expand All @@ -32,7 +32,7 @@ Here is a quick example of what circuitbreaker provides

```go
// Creates a circuit breaker that will trip if the function fails 10 times
cb := NewThresholdBreaker(10)
cb := circuit.NewThresholdBreaker(10)

events := cb.Subscribe()
go func() {
Expand All @@ -53,7 +53,7 @@ Circuitbreaker can also wrap a time out around the remote call.
```go
// Creates a circuit breaker that will trip after 10 failures or time outs
// using a time out of 5 seconds
cb := NewTimeoutBreaker(Time.Second * 5, 10)
cb := circuit.NewTimeoutBreaker(Time.Second * 5, 10)

// Proceed as above

Expand All @@ -63,7 +63,7 @@ Circuitbreaker can also trip based on the number of failures in a given time per

```go
// Creates a circuit breaker that will trip if 10 failures occur in 1 minute
cb := NewFrequencyBreaker(time.Minute, 10)
cb := circuit.NewFrequencyBreaker(time.Minute, 10)

// Proceed as above
```
Expand All @@ -74,13 +74,7 @@ time out around any request.
```go
// Passing in nil will create a regular http.Client.
// You can also build your own http.Client and pass it in
client := NewHTTPClient(time.Second * 5, 10, nil)
client.BreakerTripped = func() {
// Perhaps notify your monitoring system
}
client.BreakerReset = func() {
// Perhaps notify your monitoring system
}
client := circuit.NewHTTPClient(time.Second * 5, 10, nil)

resp, err := client.Get("http://example.com/resource.json")
```
Expand Down
44 changes: 22 additions & 22 deletions circuitbreaker.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Package circuitbreaker implements the Circuit Breaker pattern. It will wrap
// Package circuit implements the Circuit Breaker pattern. It will wrap
// a function call (typically one which uses remote services) and monitors for
// failures and/or time outs. When a threshold of failures or time outs has been
// reached, future calls to the function will not run. During this state, the
// breaker will periodically allow the function to run and, if it is successful,
// will start running the function again.
//
// Circuitbreaker includes three types of circuit breakers:
// Circuit includes three types of circuit breakers:
//
// A ThresholdBreaker will trip when the failure count reaches a given threshold.
// It does not matter how long it takes to reach the threshold.
Expand All @@ -22,9 +22,9 @@
// are all that is typically needed.
//
// The package also provides a wrapper around an http.Client that wraps all of
// the http.Client functions with a CircuitBreaker.
// the http.Client functions with a Breaker.
//
package circuitbreaker
package circuit

import (
"errors"
Expand Down Expand Up @@ -56,9 +56,9 @@ var (
ErrBreakerTimeout = errors.New("breaker time out")
)

var noop = &noOpCircuitBreaker{}
var noop = &noOpBreaker{}

type CircuitBreaker interface {
type Breaker interface {
Call(func() error) error
Fail()
Failures() int64
Expand All @@ -73,7 +73,7 @@ type CircuitBreaker interface {
// TrippableBreaker is a base for building trippable circuit breakers. It keeps
// track of the tripped state and runs the OnTrip and OnReset callbacks.
type TrippableBreaker struct {
// ResetTimeout is the minimum amount of time the CircuitBreaker will wait
// ResetTimeout is the minimum amount of time the Breaker will wait
// before allowing the function to be called again
ResetTimeout time.Duration

Expand Down Expand Up @@ -208,7 +208,7 @@ type FrequencyBreaker struct {
// Duration is the amount of time in which the failure theshold must be met.
Duration time.Duration

// Threshold is the number of failures CircuitBreaker will allow before tripping
// Threshold is the number of failures Breaker will allow before tripping
Threshold int64

_failureTick unsafe.Pointer
Expand Down Expand Up @@ -290,7 +290,7 @@ func (cb *FrequencyBreaker) failureTick() time.Time {
return *(*time.Time)(ptr)
}

// ThresholdBreaker is a ResettingCircuitBreaker that will trip when its failure count
// ThresholdBreaker is a circuit breaker that will trip when its failure count
// passes a given threshold. Clients of ThresholdBreaker can either manually call the
// Fail function to record a failure, checking the tripped state themselves, or they
// can use the Call function to wrap the ThresholdBreaker around a function call.
Expand All @@ -307,7 +307,7 @@ func NewThresholdBreaker(threshold int64) *ThresholdBreaker {
// it is protecting takes too long to run. Clients of Timeout must use the Call function.
// The Fail function is a noop.
type TimeoutBreaker struct {
// Timeout is the length of time the CircuitBreaker will wait for Call() to finish
// Timeout is the length of time the Breaker will wait for Call() to finish
Timeout time.Duration
*ThresholdBreaker
}
Expand Down Expand Up @@ -350,23 +350,23 @@ func (cb *TimeoutBreaker) Call(circuit func() error) error {
}
}

// NoOp returns a CircuitBreaker null object. It implements the interface with
// NoOp returns a Breaker null object. It implements the interface with
// no-ops for every function.
func NoOp() CircuitBreaker {
func NoOp() Breaker {
return noop
}

type noOpCircuitBreaker struct{}
type noOpBreaker struct{}

func (c *noOpCircuitBreaker) Call(f func() error) error {
func (c *noOpBreaker) Call(f func() error) error {
return f()
}

func (c *noOpCircuitBreaker) Fail() {}
func (c *noOpCircuitBreaker) Trip() {}
func (c *noOpCircuitBreaker) Reset() {}
func (c *noOpCircuitBreaker) Break() {}
func (c *noOpCircuitBreaker) Failures() int64 { return 0 }
func (c *noOpCircuitBreaker) Ready() bool { return true }
func (c *noOpCircuitBreaker) Tripped() bool { return false }
func (cb *noOpCircuitBreaker) Subscribe() <-chan BreakerEvent { return nil }
func (c *noOpBreaker) Fail() {}
func (c *noOpBreaker) Trip() {}
func (c *noOpBreaker) Reset() {}
func (c *noOpBreaker) Break() {}
func (c *noOpBreaker) Failures() int64 { return 0 }
func (c *noOpBreaker) Ready() bool { return true }
func (c *noOpBreaker) Tripped() bool { return false }
func (cb *noOpBreaker) Subscribe() <-chan BreakerEvent { return nil }
48 changes: 27 additions & 21 deletions circuitbreaker_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package circuitbreaker
package circuit

import (
"fmt"
"sync/atomic"
"testing"
"time"
)

func TestCircuitBreakerTripping(t *testing.T) {
func TestBreakerTripping(t *testing.T) {
cb := &TrippableBreaker{}

if cb.Tripped() {
Expand All @@ -24,8 +25,8 @@ func TestCircuitBreakerTripping(t *testing.T) {
}
}

func TestCircuitBreakerEvents(t *testing.T) {
cb := NewTrippableBreaker(time.Millisecond * 100)
func TestBreakerEvents(t *testing.T) {
cb := NewTrippableBreaker(time.Millisecond)
events := cb.Subscribe()

cb.Trip()
Expand All @@ -51,7 +52,7 @@ func TestCircuitBreakerEvents(t *testing.T) {
}

func TestTrippableBreakerState(t *testing.T) {
cb := NewTrippableBreaker(time.Millisecond * 100)
cb := NewTrippableBreaker(time.Millisecond)

if !cb.Ready() {
t.Fatal("expected breaker to be ready")
Expand All @@ -74,17 +75,17 @@ func TestTrippableBreakerState(t *testing.T) {
}

func TestTrippableBreakerManualBreak(t *testing.T) {
cb := NewTrippableBreaker(time.Millisecond * 10)
cb := NewTrippableBreaker(time.Millisecond)
cb.Break()
time.Sleep(time.Millisecond * 11)
time.Sleep(time.Millisecond)

if cb.Ready() {
t.Fatal("expected breaker to still be tripped")
}

cb.Reset()
cb.Trip()
time.Sleep(time.Millisecond * 11)
time.Sleep(time.Millisecond)
if !cb.Ready() {
t.Fatal("expected breaker to be ready")
}
Expand Down Expand Up @@ -154,13 +155,13 @@ func TestThresholdBreakerResets(t *testing.T) {
}

cb := NewThresholdBreaker(1)
cb.ResetTimeout = time.Millisecond * 100
cb.ResetTimeout = time.Millisecond
err := cb.Call(circuit)
if err == nil {
t.Fatal("Expected cb to return an error")
}

time.Sleep(time.Millisecond * 100)
time.Sleep(cb.ResetTimeout)
err = cb.Call(circuit)
if err != nil {
t.Fatal("Expected cb to be successful")
Expand All @@ -172,14 +173,14 @@ func TestThresholdBreakerResets(t *testing.T) {
}

func TestTimeoutBreaker(t *testing.T) {
called := 0
var called int32 = 0
circuit := func() error {
called++
time.Sleep(time.Millisecond * 150)
atomic.AddInt32(&called, 1)
time.Sleep(time.Millisecond)
return nil
}

cb := NewTimeoutBreaker(time.Millisecond*100, 1)
cb := NewTimeoutBreaker(time.Millisecond, 1)
err := cb.Call(circuit)
if err == nil {
t.Fatal("expected timeout breaker to return an error")
Expand All @@ -206,13 +207,13 @@ func TestFrequencyBreakerTripping(t *testing.T) {
}

func TestFrequencyBreakerNotTripping(t *testing.T) {
cb := NewFrequencyBreaker(time.Millisecond*100, 2)
cb := NewFrequencyBreaker(time.Millisecond, 2)
circuit := func() error {
return fmt.Errorf("error")
}

cb.Call(circuit)
time.Sleep(time.Millisecond * 105)
time.Sleep(time.Millisecond)
cb.Call(circuit)

if cb.Tripped() {
Expand All @@ -221,12 +222,12 @@ func TestFrequencyBreakerNotTripping(t *testing.T) {
}

func TestFrequencyBreakerFailures(t *testing.T) {
cb := NewFrequencyBreaker(time.Millisecond*100, 5)
cb := NewFrequencyBreaker(time.Millisecond, 5)
cb.Fail()
if f := cb.Failures(); f != 1 {
t.Fatalf("expected failure count of 1, got %d", f)
}
time.Sleep(time.Millisecond * 100)
time.Sleep(time.Millisecond)
if f := cb.Failures(); f != 0 {
t.Fatalf("expected failures count to be 0, got %d", f)
}
Expand All @@ -238,8 +239,8 @@ func TestFrequencyBreakerFailures(t *testing.T) {
}
}

func TestCircuitBreakerInterface(t *testing.T) {
var cb CircuitBreaker
func TestBreakerInterface(t *testing.T) {
var cb Breaker
cb = NewTrippableBreaker(0)
if _, ok := cb.(*TrippableBreaker); !ok {
t.Errorf("%v is not a ResettingBreaker", cb)
Expand All @@ -255,8 +256,13 @@ func TestCircuitBreakerInterface(t *testing.T) {
t.Errorf("%v is not a TimeoutBreaker", cb)
}

cb = NewFrequencyBreaker(0, 0)
if _, ok := cb.(*FrequencyBreaker); !ok {
t.Errorf("%v is not a FrequencyBreaker", cb)
}

cb = NoOp()
if _, ok := cb.(*noOpCircuitBreaker); !ok {
if _, ok := cb.(*noOpBreaker); !ok {
t.Errorf("%v is not a no-op breaker", cb)
}
}
16 changes: 8 additions & 8 deletions client.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package circuitbreaker
package circuit

import (
"io"
Expand All @@ -16,13 +16,13 @@ type HTTPClient struct {
Client *http.Client
BreakerTripped func()
BreakerReset func()
BreakerLookup func(*HTTPClient, interface{}) CircuitBreaker
BreakerLookup func(*HTTPClient, interface{}) Breaker
Panel *Panel
}

var defaultBreakerName = "_default"

// NewCircuitBreakerClient provides a circuit breaker wrapper around http.Client.
// NewHTTPClient provides a circuit breaker wrapper around http.Client.
// It wraps all of the regular http.Client functions. Specifying 0 for timeout will
// give a breaker that does not check for time outs.
func NewHTTPClient(timeout time.Duration, threshold int64, client *http.Client) *HTTPClient {
Expand All @@ -37,7 +37,7 @@ func NewHTTPClient(timeout time.Duration, threshold int64, client *http.Client)
func NewHostBasedHTTPClient(timeout time.Duration, threshold int64, client *http.Client) *HTTPClient {
brclient := NewHTTPClient(timeout, threshold, client)

brclient.BreakerLookup = func(c *HTTPClient, val interface{}) CircuitBreaker {
brclient.BreakerLookup = func(c *HTTPClient, val interface{}) Breaker {
rawURL := val.(string)
parsedURL, err := url.Parse(rawURL)
if err != nil {
Expand All @@ -59,8 +59,8 @@ func NewHostBasedHTTPClient(timeout time.Duration, threshold int64, client *http
}

// NewHTTPClientWithBreaker provides a circuit breaker wrapper around http.Client.
// It wraps all of the regular http.Client functions using the provided CircuitBreaker.
func NewHTTPClientWithBreaker(breaker CircuitBreaker, client *http.Client) *HTTPClient {
// It wraps all of the regular http.Client functions using the provided Breaker.
func NewHTTPClientWithBreaker(breaker Breaker, client *http.Client) *HTTPClient {
if client == nil {
client = &http.Client{}
}
Expand All @@ -69,7 +69,7 @@ func NewHTTPClientWithBreaker(breaker CircuitBreaker, client *http.Client) *HTTP
panel.Add(defaultBreakerName, breaker)

brclient := &HTTPClient{Client: client, Panel: NewPanel()}
brclient.BreakerLookup = func(c *HTTPClient, val interface{}) CircuitBreaker {
brclient.BreakerLookup = func(c *HTTPClient, val interface{}) Breaker {
cb, _ := c.Panel.Get(defaultBreakerName)
return cb
}
Expand Down Expand Up @@ -148,7 +148,7 @@ func (c *HTTPClient) PostForm(url string, data url.Values) (*http.Response, erro
return resp, err
}

func (c *HTTPClient) breakerLookup(val interface{}) CircuitBreaker {
func (c *HTTPClient) breakerLookup(val interface{}) Breaker {
if c.BreakerLookup != nil {
return c.BreakerLookup(c, val)
}
Expand Down
4 changes: 2 additions & 2 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package circuitbreaker
package circuit

import (
"fmt"
Expand Down Expand Up @@ -77,7 +77,7 @@ func ExampleHTTPClient() {
fmt.Printf("%s", resource)
}

func ExampleCircuitBreaker_events() {
func ExampleBreaker_events() {
// This example demonstrates the BreakerTripped and BreakerReset callbacks. These are
// available on all breaker types.
breaker := NewThresholdBreaker(1)
Expand Down
Loading

0 comments on commit c834a51

Please sign in to comment.