blob: 17fc595d58f530666c213c317f569cda32574844 (
plain) (
blame)
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
|
package document
import (
"math"
"sync/atomic"
"time"
)
type CircuitState int
const (
// CircuitClosed represents a closed circuit. Documents are processed successfully
CircuitClosed CircuitState = iota
// CircuitHalfOpen represents a half-open circuit. Some errors have happend, but processing may still recover
CircuitHalfOpen
// CircuitOpen represents a open circuit. Something is broken. We should no longer process documents
CircuitOpen
)
type CircuitBreaker interface {
Success()
Error(error)
State() CircuitState
}
type timeCircuitBreaker struct {
graceDuration time.Duration
doomDuration time.Duration
failingSinceMillis int64
lastError atomic.Value
halfOpen atomic.Value
open atomic.Value
now func() time.Time
}
func (b *timeCircuitBreaker) Success() {
atomic.StoreInt64(&b.failingSinceMillis, math.MaxInt64)
if !b.open.Load().(bool) {
b.halfOpen.CompareAndSwap(true, false)
}
}
func (b *timeCircuitBreaker) Error(err error) {
if atomic.CompareAndSwapInt64(&b.failingSinceMillis, math.MaxInt64, b.now().UnixMilli()) {
b.lastError.Store(err)
}
}
func (b *timeCircuitBreaker) State() CircuitState {
failingDuration := b.now().Sub(time.UnixMilli(atomic.LoadInt64(&b.failingSinceMillis)))
if failingDuration > b.graceDuration {
b.halfOpen.CompareAndSwap(false, true)
}
if b.doomDuration > 0 && failingDuration > b.doomDuration {
b.open.CompareAndSwap(false, true)
}
if b.open.Load().(bool) {
return CircuitOpen
} else if b.halfOpen.Load().(bool) {
return CircuitHalfOpen
}
return CircuitClosed
}
func NewCircuitBreaker(graceDuration, doomDuration time.Duration) *timeCircuitBreaker {
b := &timeCircuitBreaker{
graceDuration: graceDuration,
doomDuration: doomDuration,
now: time.Now,
failingSinceMillis: math.MaxInt64,
}
b.open.Store(false)
b.halfOpen.Store(false)
return b
}
|