-
Notifications
You must be signed in to change notification settings - Fork 353
/
Copy pathbreaker.go
172 lines (141 loc) · 3.81 KB
/
breaker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package circuit
import (
"fmt"
"strconv"
"strings"
"time"
)
// BreakerType defines the type of the used breaker: consecutive, rate or disabled.
type BreakerType int
func (b *BreakerType) UnmarshalYAML(unmarshal func(interface{}) error) error {
var value string
if err := unmarshal(&value); err != nil {
return err
}
switch value {
case "consecutive":
*b = ConsecutiveFailures
case "rate":
*b = FailureRate
case "disabled":
*b = BreakerDisabled
default:
return fmt.Errorf("invalid breaker type %v (allowed values are: consecutive, rate or disabled)", value)
}
return nil
}
const (
BreakerNone BreakerType = iota
ConsecutiveFailures
FailureRate
BreakerDisabled
)
// BreakerSettings contains the settings for individual circuit breakers.
//
// See the package overview for the detailed merging/overriding rules of the settings and for the meaning of the
// individual fields.
type BreakerSettings struct {
Type BreakerType `yaml:"type"`
Host string `yaml:"host"`
Window int `yaml:"window"`
Failures int `yaml:"failures"`
Timeout time.Duration `yaml:"timeout"`
HalfOpenRequests int `yaml:"half-open-requests"`
IdleTTL time.Duration `yaml:"idle-ttl"`
}
type breakerImplementation interface {
Allow() (func(bool), bool)
}
type voidBreaker struct{}
// Breaker represents a single circuit breaker for a particular set of settings.
//
// Use the Get() method of the Registry to request fully initialized breakers.
type Breaker struct {
settings BreakerSettings
ts time.Time
impl breakerImplementation
}
func (to BreakerSettings) mergeSettings(from BreakerSettings) BreakerSettings {
if to.Type == BreakerNone {
to.Type = from.Type
if from.Type == ConsecutiveFailures {
to.Failures = from.Failures
}
if from.Type == FailureRate {
to.Window = from.Window
to.Failures = from.Failures
}
}
if to.Timeout == 0 {
to.Timeout = from.Timeout
}
if to.HalfOpenRequests == 0 {
to.HalfOpenRequests = from.HalfOpenRequests
}
if to.IdleTTL == 0 {
to.IdleTTL = from.IdleTTL
}
return to
}
// String returns the string representation of a particular set of settings.
//
//lint:ignore ST1016 "s" makes sense here and mergeSettings has "to"
func (s BreakerSettings) String() string {
var ss []string
switch s.Type {
case ConsecutiveFailures:
ss = append(ss, "type=consecutive")
case FailureRate:
ss = append(ss, "type=rate")
case BreakerDisabled:
return "disabled"
default:
return "none"
}
if s.Host != "" {
ss = append(ss, "host="+s.Host)
}
if s.Type == FailureRate && s.Window > 0 {
ss = append(ss, "window="+strconv.Itoa(s.Window))
}
if s.Failures > 0 {
ss = append(ss, "failures="+strconv.Itoa(s.Failures))
}
if s.Timeout > 0 {
ss = append(ss, "timeout="+s.Timeout.String())
}
if s.HalfOpenRequests > 0 {
ss = append(ss, "half-open-requests="+strconv.Itoa(s.HalfOpenRequests))
}
if s.IdleTTL > 0 {
ss = append(ss, "idle-ttl="+s.IdleTTL.String())
}
return strings.Join(ss, ",")
}
func (b voidBreaker) Allow() (func(bool), bool) {
return func(bool) {}, true
}
func newBreaker(s BreakerSettings) *Breaker {
var impl breakerImplementation
switch s.Type {
case ConsecutiveFailures:
impl = newConsecutive(s)
case FailureRate:
impl = newRate(s)
default:
impl = voidBreaker{}
}
return &Breaker{
settings: s,
impl: impl,
}
}
// Allow returns true if the breaker is in the closed state and a callback function for reporting the outcome of
// the operation. The callback expects true values if the outcome of the request was successful. Allow may not
// return a callback function when the state is open.
func (b *Breaker) Allow() (func(bool), bool) {
return b.impl.Allow()
}
func (b *Breaker) idle(now time.Time) bool {
return now.Sub(b.ts) > b.settings.IdleTTL
}