Service health check
If your service relies on a dependency, you might want to periodically check its state.
When the do.HealthCheck[type]()
or the injector.HealthCheck()
function is called, the framework triggers HealthCheck
method of each service implementing a do.Healthchecker
interface, in reverse invocation order.
🕵️ Service health can be checked individually or globally. Requesting a health check on a nested scope will run checks on ancestors.
Lazy services that were not invoked, are not checked.
Trigger health check
A health check can be triggered for a root injector:
// returns the status (error or nil) for each service
injector.HealthCheck() map[string]error
injector.HealthCheckWithContext(context.Context) map[string]error
...on a single service:
// returns error on failure
do.HealthCheck[T any](do.Injector) error
do.HealthCheckWithContext[T any](context.Context, do.Injector) error
do.HealthCheckNamed[T any](do.Injector, string) error
do.HealthCheckNamedWithContext[T any](context.Context, do.Injector, string) error
Healthchecker interfaces
Your service can implement one of the following signatures:
type Healthchecker interface {
HealthCheck() error
}
type HealthcheckerWithContext interface {
HealthCheck(context.Context) error
}
Example:
// Ensure at compile-time MyService implements do.HealthcheckerWithContext
var _ do.HealthcheckerWithContext = (*MyService)(nil)
type MyService struct {}
func (*MyService) HealthCheck(context.Context) error {
// ...
return nil
}
i := do.New()
Provide(i, ...)
Invoke(i, ...)
ctx := context.WithTimeout(10 * time.Second)
i.HealthCheckWithContext(ctx)
Healthcheck options
The root scope can be created with health check parameters, for controlling parallelism or timeouts.
do.InjectorOpts{
// ...
// By default, heath checks will be triggered concurrently.
// HealthCheckParallelism==1 will trigger sequential checks.
HealthCheckParallelism uint
// When many services are checked at the same time.
HealthCheckGlobalTimeout: time.Duration
HealthCheckTimeout: time.Duration
}
Example:
type MyPostgresqlConnection struct {
DB *sql.DB
}
func (pg *MyPostgresqlConnection) Healthcheck() error {
return pg.DB.Ping() // <- might be very slow
}
i := do.NewWithOpts(&do.InjectorOpts{
HealthCheckParallelism: 100,
HealthCheckGlobalTimeout: 1 * time.Second,
HealthCheckTimeout: 100 * time.Millisecond,
})
Provide(i, NewMyPostgresqlConnection)
_ = MustInvoke(i, *MyPostgresqlConnection)
status := i.HealthCheckWithContext(ctx)
// {
// "*github.com/samber/example.MyPostgresqlConnection": "DI: health check timeout: context deadline exceeded",
// }