Codebase list golang-github-go-kit-kit / 2cddaa7
circuitbreaker: sony/gobreaker implementation Peter Bourgon 8 years ago
2 changed file(s) with 98 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 package circuitbreaker
1
2 import (
3 "github.com/sony/gobreaker"
4 "golang.org/x/net/context"
5
6 "github.com/go-kit/kit/endpoint"
7 )
8
9 // NewSonyCircuitBreaker returns an endpoint.Middleware that permits the
10 // request if the underlying circuit breaker allows it. Only errors returned
11 // by the wrapped endpoint count against the circuit breaker's error count.
12 // See github.com/sony/gobreaker for more information.
13 func NewSonyCircuitBreaker(settings gobreaker.Settings) endpoint.Middleware {
14 cb := gobreaker.NewCircuitBreaker(settings)
15 return func(next endpoint.Endpoint) endpoint.Endpoint {
16 return func(ctx context.Context, request interface{}) (interface{}, error) {
17 return cb.Execute(func() (interface{}, error) { return next(ctx, request) })
18 }
19 }
20 }
0 package circuitbreaker_test
1
2 import (
3 "errors"
4 "testing"
5 "time"
6
7 "github.com/sony/gobreaker"
8 "golang.org/x/net/context"
9
10 "github.com/go-kit/kit/circuitbreaker"
11 "github.com/go-kit/kit/endpoint"
12 )
13
14 func TestSonyCircuitBreaker(t *testing.T) {
15 var (
16 thru int
17 last gobreaker.State
18 myError = errors.New("❤️")
19 timeout = time.Millisecond
20 stateChange = func(_ string, from, to gobreaker.State) { last = to }
21 )
22
23 var e endpoint.Endpoint
24 e = func(context.Context, interface{}) (interface{}, error) { thru++; return struct{}{}, myError }
25 e = circuitbreaker.NewSonyCircuitBreaker(gobreaker.Settings{
26 Timeout: timeout,
27 OnStateChange: stateChange,
28 })(e)
29
30 // "Default ReadyToTrip returns true when the number of consecutive
31 // failures is more than 5."
32 // https://github.com/sony/gobreaker/blob/bfa846d/gobreaker.go#L76
33 for i := 0; i < 5; i++ {
34 if _, err := e(context.Background(), struct{}{}); err != myError {
35 t.Errorf("want %v, have %v", myError, err)
36 }
37 }
38
39 if want, have := 5, thru; want != have {
40 t.Errorf("want %d, have %d", want, have)
41 }
42
43 e(context.Background(), struct{}{})
44 if want, have := 6, thru; want != have { // got thru
45 t.Errorf("want %d, have %d", want, have)
46 }
47 if want, have := gobreaker.StateOpen, last; want != have { // tripped
48 t.Errorf("want %v, have %v", want, have)
49 }
50
51 e(context.Background(), struct{}{})
52 if want, have := 6, thru; want != have { // didn't get thru
53 t.Errorf("want %d, have %d", want, have)
54 }
55
56 time.Sleep(2 * timeout)
57
58 e(context.Background(), struct{}{})
59 if want, have := 7, thru; want != have { // got thru via halfopen
60 t.Errorf("want %d, have %d", want, have)
61 }
62 if want, have := gobreaker.StateOpen, last; want != have { // re-tripped
63 t.Errorf("want %v, have %v", want, have)
64 }
65
66 time.Sleep(2 * timeout)
67
68 myError = nil
69 e(context.Background(), struct{}{})
70 if want, have := 8, thru; want != have { // got thru via halfopen
71 t.Errorf("want %d, have %d", want, have)
72 }
73 if want, have := gobreaker.StateClosed, last; want != have { // now it's good
74 t.Errorf("want %v, have %v", want, have)
75 }
76 }