github.com/xmidt-org/webpa-common@v1.11.9/xhttp/gate/gate.go (about) 1 package gate 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 8 "github.com/go-kit/kit/metrics/discard" 9 "github.com/xmidt-org/webpa-common/xmetrics" 10 ) 11 12 const ( 13 // Open is the value a gauge is set to that indicates the gate is open 14 Open float64 = 1.0 15 16 // Closed is the value a gauge is set to that indicates the gate is closed 17 Closed float64 = 0.0 18 ) 19 20 // Interface represents a concurrent condition indicating whether HTTP traffic should be allowed. 21 // This type essentially represents an atomic boolean with some extra functionality, such as metrics gathering. 22 type Interface interface { 23 fmt.Stringer 24 25 // Raise opens this gate. If the gate was raised as a result, this method returns true. If the 26 // gate was already raised, this method returns false. 27 Raise() bool 28 29 // Lower closes this gate. If the gate was lowered as a result, this method returns true. If the 30 // gate was already lowered, this method returns false. 31 Lower() bool 32 33 // Open tests if this gate is open 34 Open() bool 35 36 // State returns the current state (true for open, false for closed) along with the time 37 // at which this gate entered that state. 38 State() (bool, time.Time) 39 } 40 41 // GateOption is a configuration option for a gate Interface 42 type GateOption func(*gate) 43 44 // WithGauge configures a gate with a metrics Gauge that tracks the state of the gate. 45 func WithGauge(gauge xmetrics.Setter) GateOption { 46 return func(g *gate) { 47 if gauge != nil { 48 g.state = gauge 49 } else { 50 g.state = discard.NewGauge() 51 } 52 } 53 } 54 55 // New constructs a gate Interface with zero or more options. The returned gate takes on the given 56 // initial state, and any configured gauge is updated to reflect this initial state. 57 func New(initial bool, options ...GateOption) Interface { 58 g := &gate{ 59 open: initial, 60 now: time.Now, 61 state: discard.NewGauge(), 62 } 63 64 for _, o := range options { 65 o(g) 66 } 67 68 if g.open { 69 g.state.Set(Open) 70 } else { 71 g.state.Set(Closed) 72 } 73 74 g.timestamp = g.now().UTC() 75 return g 76 } 77 78 // gate is the internal Interface implementation 79 type gate struct { 80 lock sync.RWMutex 81 open bool 82 timestamp time.Time 83 now func() time.Time 84 85 state xmetrics.Setter 86 } 87 88 func (g *gate) Raise() bool { 89 defer g.lock.Unlock() 90 g.lock.Lock() 91 92 if g.open { 93 return false 94 } 95 96 g.open = true 97 g.state.Set(Open) 98 g.timestamp = g.now().UTC() 99 return true 100 } 101 102 func (g *gate) Lower() bool { 103 defer g.lock.Unlock() 104 g.lock.Lock() 105 106 if !g.open { 107 return false 108 } 109 110 g.open = false 111 g.state.Set(Closed) 112 g.timestamp = g.now().UTC() 113 return true 114 } 115 116 func (g *gate) Open() bool { 117 g.lock.RLock() 118 open := g.open 119 g.lock.RUnlock() 120 121 return open 122 } 123 124 func (g *gate) State() (bool, time.Time) { 125 g.lock.RLock() 126 open := g.open 127 timestamp := g.timestamp 128 g.lock.RUnlock() 129 130 return open, timestamp 131 } 132 133 func (g *gate) String() string { 134 if g.Open() { 135 return "open" 136 } else { 137 return "closed" 138 } 139 }