mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
See https://github.com/grafana/dskit/issues/576 Signed-off-by: Daniel Strobusch <[email protected]>
101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
package services
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
errFailureWatcherNotInitialized = errors.New("FailureWatcher has not been initialized")
|
|
errFailureWatcherClosed = errors.New("FailureWatcher has been stopped")
|
|
)
|
|
|
|
// FailureWatcher waits for service failures, and passed them to the channel.
|
|
type FailureWatcher struct {
|
|
mu sync.Mutex
|
|
ch chan error
|
|
closed bool
|
|
unregisterListeners []func()
|
|
}
|
|
|
|
func NewFailureWatcher() *FailureWatcher {
|
|
return &FailureWatcher{ch: make(chan error)}
|
|
}
|
|
|
|
// Chan returns channel for this watcher. If watcher is nil, returns nil channel.
|
|
// Errors returned on the channel include failure case and service description.
|
|
func (w *FailureWatcher) Chan() <-chan error {
|
|
// Graceful handle the case FailureWatcher has not been initialized,
|
|
// to simplify the code in the components using it.
|
|
if w == nil {
|
|
return nil
|
|
}
|
|
return w.ch
|
|
}
|
|
|
|
func (w *FailureWatcher) WatchService(service Service) {
|
|
// Ensure that if the caller request to watch a service, then the FailureWatcher
|
|
// has been initialized.
|
|
if w == nil {
|
|
panic(errFailureWatcherNotInitialized)
|
|
}
|
|
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
if w.closed {
|
|
panic(errFailureWatcherClosed)
|
|
}
|
|
|
|
stop := service.AddListener(NewListener(nil, nil, nil, nil, func(_ State, failure error) {
|
|
w.ch <- errors.Wrapf(failure, "service %s failed", DescribeService(service))
|
|
}))
|
|
w.unregisterListeners = append(w.unregisterListeners, stop)
|
|
}
|
|
|
|
func (w *FailureWatcher) WatchManager(manager *Manager) {
|
|
// Ensure that if the caller request to watch services, then the FailureWatcher
|
|
// has been initialized.
|
|
if w == nil {
|
|
panic(errFailureWatcherNotInitialized)
|
|
}
|
|
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
if w.closed {
|
|
panic(errFailureWatcherClosed)
|
|
}
|
|
|
|
stop := manager.AddListener(NewManagerListener(nil, nil, func(service Service) {
|
|
w.ch <- errors.Wrapf(service.FailureCase(), "service %s failed", DescribeService(service))
|
|
}))
|
|
w.unregisterListeners = append(w.unregisterListeners, stop)
|
|
}
|
|
|
|
// Close stops this failure watcher and closes channel returned by Chan() method. After closing failure watcher,
|
|
// it cannot be used to watch additional services or managers.
|
|
// Repeated calls to Close() do nothing.
|
|
func (w *FailureWatcher) Close() {
|
|
// Graceful handle the case FailureWatcher has not been initialized,
|
|
// to simplify the code in the components using it.
|
|
if w == nil {
|
|
return
|
|
}
|
|
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
if w.closed {
|
|
return
|
|
}
|
|
for _, stop := range w.unregisterListeners {
|
|
stop()
|
|
}
|
|
|
|
// All listeners are now stopped, and can't receive more notifications. We can close the channel.
|
|
close(w.ch)
|
|
w.closed = true
|
|
}
|