Codebase list golang-gopkg-eapache-go-resiliency.v1 / 5e88755
First draft of circuit-breaker Evan Huus 9 years ago
1 changed file(s) with 110 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 package breaker
1
2 import (
3 "errors"
4 "fmt"
5 "sync"
6 "time"
7 )
8
9 var BreakerOpen = errors.New("circuit breaker is open")
10
11 type Panic struct {
12 Value interface{}
13 }
14
15 func (p Panic) Error() string {
16 return fmt.Sprint("panic:", p.Value)
17 }
18
19 type state int
20
21 const (
22 closed state = iota
23 open
24 halfOpen
25 )
26
27 type Breaker struct {
28 errorThreshold, successThreshold int
29 timeout time.Duration
30
31 lock sync.RWMutex
32 state state
33 errors, successes int
34 }
35
36 func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker {
37 return &Breaker{
38 errorThreshold: errorThreshold,
39 successThreshold: successThreshold,
40 timeout: timeout,
41 }
42 }
43
44 func (b *Breaker) Run(x func() error) error {
45 b.lock.RLock()
46 state := b.state
47 b.lock.RUnlock()
48
49 if state == open {
50 return BreakerOpen
51 }
52
53 result := func() (err error) {
54 defer func() {
55 if val := recover(); val != nil {
56 err = Panic{Value: val}
57 }
58 }()
59 return x()
60 }()
61
62 b.lock.Lock()
63 defer b.lock.Unlock()
64
65 if result == nil {
66 if b.state == halfOpen {
67 b.successes++
68 if b.successes == b.successThreshold {
69 b.closeBreaker()
70 }
71 }
72 } else {
73 switch b.state {
74 case closed:
75 b.errors++
76 if b.errors == b.errorThreshold {
77 b.openBreaker()
78 }
79 case halfOpen:
80 b.openBreaker()
81 }
82 }
83
84 return result
85 }
86
87 func (b *Breaker) openBreaker() {
88 b.changeState(open)
89 go b.timer()
90 }
91
92 func (b *Breaker) closeBreaker() {
93 b.changeState(closed)
94 }
95
96 func (b *Breaker) timer() {
97 time.Sleep(b.timeout)
98
99 b.lock.Lock()
100 defer b.lock.Unlock()
101
102 b.changeState(halfOpen)
103 }
104
105 func (b *Breaker) changeState(newState state) {
106 b.errors = 0
107 b.successes = 0
108 b.state = newState
109 }