Documentation ¶
Index ¶
- Constants
- Variables
- func CloseAll(cs ...io.Closer) error
- func CopyHealth(dest, src map[string]error)
- func MultiCloser[C io.Closer](cs []C) io.Closer
- func NewTicker(period time.Duration) *timeutil.Ticker
- type ErrorBuffer
- type HealthChecker
- func (c *HealthChecker) Close() error
- func (c *HealthChecker) IsHealthy() (healthy bool, errors map[string]error)
- func (c *HealthChecker) IsReady() (ready bool, errors map[string]error)
- func (c *HealthChecker) Register(service HealthReporter) error
- func (c *HealthChecker) Start() error
- func (c *HealthChecker) Unregister(name string) error
- type HealthReporter
- type MultiStart
- type Service
- type StartClose
- type StateMachine
- func (once *StateMachine) Healthy() error
- func (once *StateMachine) IfNotStopped(f func()) (ok bool)
- func (once *StateMachine) IfStarted(f func()) (ok bool)
- func (once *StateMachine) Ready() error
- func (once *StateMachine) StartOnce(name string, fn func() error) error
- func (once *StateMachine) State() string
- func (once *StateMachine) StopOnce(name string, fn func() error) error
- type StopChan
- type StopRChan
- type TickerConfig
Examples ¶
Constants ¶
const DefaultJitter timeutil.JitterPct = 0.1
DefaultJitter is +/-10%
Variables ¶
var ( ErrAlreadyStopped = errors.New("already stopped") ErrCannotStopUnstarted = errors.New("cannot stop unstarted service") )
Functions ¶
func CloseAll ¶
CloseAll closes all elements concurrently. Use this when you have various different types of io.Closer.
func CopyHealth ¶
CopyHealth copies health statuses from src to dest. Useful when implementing HealthReporter.HealthReport. If duplicate names are encountered, the errors are joined, unless testing in which case a panic is thrown.
func MultiCloser ¶
MultiCloser returns an io.Closer which closes all elements concurrently. Use this when you have a slice of a type which implements io.Closer. []io.Closer can be cast directly to MultiCloser.
Example ¶
ctx := context.Background() f1 := CloseFailure("f") f2 := CloseFailure("f") cs := []CloseFailure{f1, f2} var ms MultiStart if err := ms.Start(ctx, f1, f2); err != nil { fmt.Println(err) return } mc := MultiCloser(cs) if err := mc.Close(); err != nil { fmt.Println(err) }
Output: f started f started f close failure f close failure failed to close: f failed to close: f
Types ¶
type ErrorBuffer ¶
type ErrorBuffer struct {
// contains filtered or unexported fields
}
ErrorBuffer uses joinedErrors interface to join multiple errors into a single error. This is useful to track the most recent N errors in a service and flush them as a single error.
func (*ErrorBuffer) Append ¶
func (eb *ErrorBuffer) Append(incoming error)
func (*ErrorBuffer) Flush ¶
func (eb *ErrorBuffer) Flush() (err error)
func (*ErrorBuffer) SetCap ¶
func (eb *ErrorBuffer) SetCap(cap int)
type HealthChecker ¶
type HealthChecker struct { StateMachine // contains filtered or unexported fields }
HealthChecker is a services.Service which monitors other services and can be probed for system health.
func NewChecker ¶
func NewChecker(ver, sha string) *HealthChecker
func (*HealthChecker) Close ¶
func (c *HealthChecker) Close() error
func (*HealthChecker) IsHealthy ¶
func (c *HealthChecker) IsHealthy() (healthy bool, errors map[string]error)
IsHealthy returns the current health of the system. A system is considered healthy if all checks are passing (no errors)
func (*HealthChecker) IsReady ¶
func (c *HealthChecker) IsReady() (ready bool, errors map[string]error)
IsReady returns the current readiness of the system. A system is considered ready if all checks are passing (no errors)
func (*HealthChecker) Register ¶
func (c *HealthChecker) Register(service HealthReporter) error
Register a service for health checks.
func (*HealthChecker) Start ¶
func (c *HealthChecker) Start() error
func (*HealthChecker) Unregister ¶
func (c *HealthChecker) Unregister(name string) error
Unregister a service.
type HealthReporter ¶
type HealthReporter interface { // Ready should return nil if ready, or an error message otherwise. From the k8s docs: // > ready means it’s initialized and healthy means that it can accept traffic in kubernetes // See: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ Ready() error // HealthReport returns a full health report of the callee including its dependencies. // key is the dep name, value is nil if healthy, or error message otherwise. // Use CopyHealth to collect reports from sub-services. HealthReport() map[string]error // Name returns the fully qualified name of the component. Usually the logger name. Name() string }
HealthReporter should be implemented by any type requiring health checks.
type MultiStart ¶
type MultiStart struct {
// contains filtered or unexported fields
}
MultiStart is a utility for starting multiple services together. The set of started services is tracked internally, so that they can be closed if any single service fails to start.
Example ¶
ctx := context.Background() a := Healthy("a") b := CloseFailure("b") c := WontStart("c") var ms MultiStart if err := ms.Start(ctx, a, b, c); err != nil { fmt.Println(err) }
Output: a started b started c start failure b close failure a closed failed to start: c failed to close: b
func (*MultiStart) Close ¶
func (m *MultiStart) Close() (err error)
Close closes all started services, in reverse order.
func (*MultiStart) CloseBecause ¶
func (m *MultiStart) CloseBecause(reason error) (err error)
CloseBecause calls Close and returns reason along with any additional errors.
func (*MultiStart) Start ¶
func (m *MultiStart) Start(ctx context.Context, srvcs ...StartClose) (err error)
Start attempts to Start all services. If any service fails to start, the previously started services will be Closed, and an error returned.
type Service ¶
type Service interface { // Start the service. // - Must return promptly if the context is cancelled. // - Must not retain the context after returning (only applies to start-up) // - Must not depend on external resources (no blocking network calls) Start(context.Context) error // Close stops the service. // Invariants: Usually after this call the Service cannot be started // again, you need to build a new Service to do so. Close() error HealthReporter }
Service represents a long-running service inside the Application.
Typically, a Service will leverage utils.StateMachine to implement these calls in a safe manner.
Template ¶
Mockable Foo service with a run loop
type ( // Expose a public interface so we can mock the service. Foo interface { service.Service // ... } foo struct { // ... stop chan struct{} done chan struct{} utils.StartStopOnce } ) var _ Foo = (*foo)(nil) func NewFoo() Foo { f := &foo{ // ... } return f } func (f *foo) Start(ctx context.Context) error { return f.StartOnce("Foo", func() error { go f.run() return nil }) } func (f *foo) Close() error { return f.StopOnce("Foo", func() error { // trigger goroutine cleanup close(f.stop) // wait for cleanup to complete <-f.done return nil }) } func (f *foo) run() { // signal cleanup completion defer close(f.done) for { select { // ... case <-f.stop: // stop the routine return } } }
type StartClose ¶
StartClose is a subset of the ServiceCtx interface.
type StateMachine ¶
type StateMachine struct { sync.RWMutex // lock is held during startup/shutdown, RLock is held while executing functions dependent on a particular state // SvcErrBuffer is an ErrorBuffer that let service owners track critical errors happening in the service. // // SvcErrBuffer.SetCap(int) Overrides buffer limit from defaultErrorBufferCap // SvcErrBuffer.Append(error) Appends an error to the buffer // SvcErrBuffer.Flush() error returns all tracked errors as a single joined error SvcErrBuffer ErrorBuffer // contains filtered or unexported fields }
StateMachine contains a state integer
func (*StateMachine) Healthy ¶
func (once *StateMachine) Healthy() error
Healthy returns ErrNotStarted if the state is not started. Override this per-service with more specific implementations.
func (*StateMachine) IfNotStopped ¶
func (once *StateMachine) IfNotStopped(f func()) (ok bool)
IfNotStopped runs the func and returns true if in any state other than Stopped
func (*StateMachine) IfStarted ¶
func (once *StateMachine) IfStarted(f func()) (ok bool)
IfStarted runs the func and returns true only if started, otherwise returns false
func (*StateMachine) Ready ¶
func (once *StateMachine) Ready() error
Ready returns ErrNotStarted if the state is not started.
func (*StateMachine) StartOnce ¶
func (once *StateMachine) StartOnce(name string, fn func() error) error
StartOnce sets the state to Started
func (*StateMachine) State ¶
func (once *StateMachine) State() string
State retrieves the current state
type StopChan ¶
type StopChan chan struct{}
A StopChan signals when some work should stop. Use StopChanR if you already have a read only <-chan.
func (StopChan) Ctx ¶
Ctx cancels a context.Context when StopChan is closed.
func (StopChan) CtxCancel ¶
func (s StopChan) CtxCancel(ctx context.Context, cancel context.CancelFunc) (context.Context, context.CancelFunc)
CtxCancel cancels a context.Context when StopChan is closed. Returns ctx and cancel unmodified, for convenience.
func (StopChan) NewCtx ¶
func (s StopChan) NewCtx() (context.Context, context.CancelFunc)
NewCtx returns a background context.Context that is cancelled when StopChan is closed.
type StopRChan ¶
type StopRChan <-chan struct{}
A StopRChan signals when some work should stop. This is a receive-only version of StopChan, for casting an existing <-chan.
func (StopRChan) Ctx ¶
Ctx cancels a context.Context when StopChan is closed.
func (StopRChan) CtxCancel ¶
func (s StopRChan) CtxCancel(ctx context.Context, cancel context.CancelFunc) (context.Context, context.CancelFunc)
CtxCancel cancels a context.Context when StopChan is closed. Returns ctx and cancel unmodified, for convenience.
func (StopRChan) NewCtx ¶
func (s StopRChan) NewCtx() (context.Context, context.CancelFunc)
NewCtx returns a background context.Context that is cancelled when StopChan is closed.