Skip to main content

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",
// }