github.com/lusis/distribution@v2.0.1+incompatible/notifications/sinks.go (about) 1 package notifications 2 3 import ( 4 "container/list" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/Sirupsen/logrus" 10 ) 11 12 // NOTE(stevvooe): This file contains definitions for several utility sinks. 13 // Typically, the broadcaster is the only sink that should be required 14 // externally, but others are suitable for export if the need arises. Albeit, 15 // the tight integration with endpoint metrics should be removed. 16 17 // Broadcaster sends events to multiple, reliable Sinks. The goal of this 18 // component is to dispatch events to configured endpoints. Reliability can be 19 // provided by wrapping incoming sinks. 20 type Broadcaster struct { 21 sinks []Sink 22 events chan []Event 23 closed chan chan struct{} 24 } 25 26 // NewBroadcaster ... 27 // Add appends one or more sinks to the list of sinks. The broadcaster 28 // behavior will be affected by the properties of the sink. Generally, the 29 // sink should accept all messages and deal with reliability on its own. Use 30 // of EventQueue and RetryingSink should be used here. 31 func NewBroadcaster(sinks ...Sink) *Broadcaster { 32 b := Broadcaster{ 33 sinks: sinks, 34 events: make(chan []Event), 35 closed: make(chan chan struct{}), 36 } 37 38 // Start the broadcaster 39 go b.run() 40 41 return &b 42 } 43 44 // Write accepts a block of events to be dispatched to all sinks. This method 45 // will never fail and should never block (hopefully!). The caller cedes the 46 // slice memory to the broadcaster and should not modify it after calling 47 // write. 48 func (b *Broadcaster) Write(events ...Event) error { 49 select { 50 case b.events <- events: 51 case <-b.closed: 52 return ErrSinkClosed 53 } 54 return nil 55 } 56 57 // Close the broadcaster, ensuring that all messages are flushed to the 58 // underlying sink before returning. 59 func (b *Broadcaster) Close() error { 60 logrus.Infof("broadcaster: closing") 61 select { 62 case <-b.closed: 63 // already closed 64 return fmt.Errorf("broadcaster: already closed") 65 default: 66 // do a little chan handoff dance to synchronize closing 67 closed := make(chan struct{}) 68 b.closed <- closed 69 close(b.closed) 70 <-closed 71 return nil 72 } 73 } 74 75 // run is the main broadcast loop, started when the broadcaster is created. 76 // Under normal conditions, it waits for events on the event channel. After 77 // Close is called, this goroutine will exit. 78 func (b *Broadcaster) run() { 79 for { 80 select { 81 case block := <-b.events: 82 for _, sink := range b.sinks { 83 if err := sink.Write(block...); err != nil { 84 logrus.Errorf("broadcaster: error writing events to %v, these events will be lost: %v", sink, err) 85 } 86 } 87 case closing := <-b.closed: 88 89 // close all the underlying sinks 90 for _, sink := range b.sinks { 91 if err := sink.Close(); err != nil { 92 logrus.Errorf("broadcaster: error closing sink %v: %v", sink, err) 93 } 94 } 95 closing <- struct{}{} 96 97 logrus.Debugf("broadcaster: closed") 98 return 99 } 100 } 101 } 102 103 // eventQueue accepts all messages into a queue for asynchronous consumption 104 // by a sink. It is unbounded and thread safe but the sink must be reliable or 105 // events will be dropped. 106 type eventQueue struct { 107 sink Sink 108 events *list.List 109 listeners []eventQueueListener 110 cond *sync.Cond 111 mu sync.Mutex 112 closed bool 113 } 114 115 // eventQueueListener is called when various events happen on the queue. 116 type eventQueueListener interface { 117 ingress(events ...Event) 118 egress(events ...Event) 119 } 120 121 // newEventQueue returns a queue to the provided sink. If the updater is non- 122 // nil, it will be called to update pending metrics on ingress and egress. 123 func newEventQueue(sink Sink, listeners ...eventQueueListener) *eventQueue { 124 eq := eventQueue{ 125 sink: sink, 126 events: list.New(), 127 listeners: listeners, 128 } 129 130 eq.cond = sync.NewCond(&eq.mu) 131 go eq.run() 132 return &eq 133 } 134 135 // Write accepts the events into the queue, only failing if the queue has 136 // beend closed. 137 func (eq *eventQueue) Write(events ...Event) error { 138 eq.mu.Lock() 139 defer eq.mu.Unlock() 140 141 if eq.closed { 142 return ErrSinkClosed 143 } 144 145 for _, listener := range eq.listeners { 146 listener.ingress(events...) 147 } 148 eq.events.PushBack(events) 149 eq.cond.Signal() // signal waiters 150 151 return nil 152 } 153 154 // Close shutsdown the event queue, flushing 155 func (eq *eventQueue) Close() error { 156 eq.mu.Lock() 157 defer eq.mu.Unlock() 158 159 if eq.closed { 160 return fmt.Errorf("eventqueue: already closed") 161 } 162 163 // set closed flag 164 eq.closed = true 165 eq.cond.Signal() // signal flushes queue 166 eq.cond.Wait() // wait for signal from last flush 167 168 return eq.sink.Close() 169 } 170 171 // run is the main goroutine to flush events to the target sink. 172 func (eq *eventQueue) run() { 173 for { 174 block := eq.next() 175 176 if block == nil { 177 return // nil block means event queue is closed. 178 } 179 180 if err := eq.sink.Write(block...); err != nil { 181 logrus.Warnf("eventqueue: error writing events to %v, these events will be lost: %v", eq.sink, err) 182 } 183 184 for _, listener := range eq.listeners { 185 listener.egress(block...) 186 } 187 } 188 } 189 190 // next encompasses the critical section of the run loop. When the queue is 191 // empty, it will block on the condition. If new data arrives, it will wake 192 // and return a block. When closed, a nil slice will be returned. 193 func (eq *eventQueue) next() []Event { 194 eq.mu.Lock() 195 defer eq.mu.Unlock() 196 197 for eq.events.Len() < 1 { 198 if eq.closed { 199 eq.cond.Broadcast() 200 return nil 201 } 202 203 eq.cond.Wait() 204 } 205 206 front := eq.events.Front() 207 block := front.Value.([]Event) 208 eq.events.Remove(front) 209 210 return block 211 } 212 213 // retryingSink retries the write until success or an ErrSinkClosed is 214 // returned. Underlying sink must have p > 0 of succeeding or the sink will 215 // block. Internally, it is a circuit breaker retries to manage reset. 216 // Concurrent calls to a retrying sink are serialized through the sink, 217 // meaning that if one is in-flight, another will not proceed. 218 type retryingSink struct { 219 mu sync.Mutex 220 sink Sink 221 closed bool 222 223 // circuit breaker heuristics 224 failures struct { 225 threshold int 226 recent int 227 last time.Time 228 backoff time.Duration // time after which we retry after failure. 229 } 230 } 231 232 type retryingSinkListener interface { 233 active(events ...Event) 234 retry(events ...Event) 235 } 236 237 // TODO(stevvooe): We are using circuit break here, which actually doesn't 238 // make a whole lot of sense for this use case, since we always retry. Move 239 // this to use bounded exponential backoff. 240 241 // newRetryingSink returns a sink that will retry writes to a sink, backing 242 // off on failure. Parameters threshold and backoff adjust the behavior of the 243 // circuit breaker. 244 func newRetryingSink(sink Sink, threshold int, backoff time.Duration) *retryingSink { 245 rs := &retryingSink{ 246 sink: sink, 247 } 248 rs.failures.threshold = threshold 249 rs.failures.backoff = backoff 250 251 return rs 252 } 253 254 // Write attempts to flush the events to the downstream sink until it succeeds 255 // or the sink is closed. 256 func (rs *retryingSink) Write(events ...Event) error { 257 rs.mu.Lock() 258 defer rs.mu.Unlock() 259 260 retry: 261 262 if rs.closed { 263 return ErrSinkClosed 264 } 265 266 if !rs.proceed() { 267 logrus.Warnf("%v encountered too many errors, backing off", rs.sink) 268 rs.wait(rs.failures.backoff) 269 goto retry 270 } 271 272 if err := rs.write(events...); err != nil { 273 if err == ErrSinkClosed { 274 // terminal! 275 return err 276 } 277 278 logrus.Errorf("retryingsink: error writing events: %v, retrying", err) 279 goto retry 280 } 281 282 return nil 283 } 284 285 // Close closes the sink and the underlying sink. 286 func (rs *retryingSink) Close() error { 287 rs.mu.Lock() 288 defer rs.mu.Unlock() 289 290 if rs.closed { 291 return fmt.Errorf("retryingsink: already closed") 292 } 293 294 rs.closed = true 295 return rs.sink.Close() 296 } 297 298 // write provides a helper that dispatches failure and success properly. Used 299 // by write as the single-flight write call. 300 func (rs *retryingSink) write(events ...Event) error { 301 if err := rs.sink.Write(events...); err != nil { 302 rs.failure() 303 return err 304 } 305 306 rs.reset() 307 return nil 308 } 309 310 // wait backoff time against the sink, unlocking so others can proceed. Should 311 // only be called by methods that currently have the mutex. 312 func (rs *retryingSink) wait(backoff time.Duration) { 313 rs.mu.Unlock() 314 defer rs.mu.Lock() 315 316 // backoff here 317 time.Sleep(backoff) 318 } 319 320 // reset marks a successful call. 321 func (rs *retryingSink) reset() { 322 rs.failures.recent = 0 323 rs.failures.last = time.Time{} 324 } 325 326 // failure records a failure. 327 func (rs *retryingSink) failure() { 328 rs.failures.recent++ 329 rs.failures.last = time.Now().UTC() 330 } 331 332 // proceed returns true if the call should proceed based on circuit breaker 333 // heuristics. 334 func (rs *retryingSink) proceed() bool { 335 return rs.failures.recent < rs.failures.threshold || 336 time.Now().UTC().After(rs.failures.last.Add(rs.failures.backoff)) 337 }