Skip to main content

Graceful service shutdown

If your service brings a state such as a connection pool, a buffer, etc... you might want to close or flush it gracefully on the application exit.

When the do.Shutdown[type]() or the injector.Shutdown() function is called, the framework triggers Shutdown method of each service implementing a do.Shutdowner interface, in reverse invocation order.

🛑 Services can be shutdowned properly, in back-initialization order. Requesting a shutdown on a (root?) scope shutdowns its children recursively.

Trigger shutdown

A shutdown can be triggered on a root scope:

// on demand
injector.Shutdown() error
injector.ShutdownWithContext(context.Context) error

// on signal
injector.ShutdownOnSignals(...os.Signal) (os.Signal, error)
injector.ShutdownOnSignalsWithContext(context.Context, ...os.Signal) (os.Signal, error)

...on a single service:

// returns error on failure
do.Shutdown[T any](do.Injector) error
do.ShutdownWithContext[T any](context.Context, do.Injector) error
do.ShutdownNamed[T any](do.Injector, string) error
do.ShutdownNamedWithContext[T any](context.Context, do.Injector, string) error

// panics on failure
do.MustShutdown[T any](do.Injector)
do.MustShutdownWithContext[T any](context.Context, do.Injector)
do.MustShutdownNamed[T any](do.Injector, string)
do.MustShutdownNamedWithContext[T any](context.Context, do.Injector, string)
info

If no signal is passed to injector.ShutdownOnSignals(...), both syscall.SIGTERM and os.Interrupt are handled by default.

Shutdowner interfaces

Your service can implement one of the following signatures:

type Shutdowner interface {
Shutdown()
}

type ShutdownerWithError interface {
Shutdown() error
}

type ShutdownerWithContext interface {
Shutdown(context.Context)
}

type ShutdownerWithContextAndError interface {
Shutdown(context.Context) error
}

Example:

// Ensure at compile-time MyService implements do.ShutdownerWithContextAndError
var _ do.ShutdownerWithContextAndError = (*MyService)(nil)

type MyService struct {}

func (*MyService) Shutdown(context.Context) error {
// ...
return nil
}

i := do.New()

Provide(i, ...)
Invoke(i, ...)

ctx := context.WithTimeout(10 * time.Second)
errors := i.ShutdownWithContext(ctx)
if err != nil {
log.Println("shutdown error:", err)
}