apistockdocs
v0.4 GitHub apistock.dev
Technical/Core packages

app

import "apistock.dev/app"Source on GitHub

Package app runs an application's long-running work and shuts it down in a defined order.

Constructors do blocking setup (open a pool, load keys) and register what they open on a Cleanup stack. Long-running work, such as an HTTP server or a job worker, implements Runner. Run starts the runners and, when a signal arrives, the context ends or a runner fails, performs the shutdown sequence documented on Run.

A typical composition root:

cleanup := &app.Cleanup{}
db, err := postgres.Open(ctx, dsn)
if err != nil {
	return errors.Join(err, cleanup.Close(ctx))
}
cleanup.AddCloser("postgres", db)
return app.Run(ctx, []app.Runner{server, workers}, app.WithCleanup(cleanup))

Stability: pre-1.0; the API may change in minor releases (ADR-0015). Design: ADR-0017.

Constants#

const DefaultDrainDelay, …#

go
const (
	DefaultDrainDelay      = 5 * time.Second
	DefaultShutdownTimeout = 25 * time.Second
)

Defaults used by Run.

Variables#

var ErrRunnerExited, …#

go
var (
	// ErrRunnerExited reports that a runner returned before shutdown started.
	ErrRunnerExited = errors.New("app: runner exited before shutdown")

	// ErrShutdownTimeout reports that runners did not stop within the
	// shutdown timeout.
	ErrShutdownTimeout = errors.New("app: shutdown timed out")

	// ErrForcedShutdown reports that a second signal interrupted graceful
	// shutdown.
	ErrForcedShutdown = errors.New("app: shutdown forced by signal")
)

Errors reported by Run. Check them with errors.Is.

Functions#

func Run#

go
func Run(ctx context.Context, runners []Runner, opts ...Option) error

Run starts every runner and blocks until ctx is done, a configured signal arrives, or a runner stops. It then shuts down in this order:

  1. Shutdown hooks run (for example, marking the service not ready).
  2. The drain delay elapses, giving load balancers time to stop routing. It is skipped when shutdown started because a runner stopped.
  3. The runners' context is cancelled; Run waits up to the shutdown timeout.
  4. The cleanup stack closes resources in reverse order of registration.

A signal received during shutdown skips the remaining waits and adds ErrForcedShutdown. Runners receive a context that keeps ctx's values but is cancelled only in step 3.

Run returns nil after a clean shutdown. Otherwise it returns every failure joined: runner errors, ErrRunnerExited, ErrShutdownTimeout, ErrForcedShutdown and cleanup errors. Runner errors that wrap context.Canceled after step 3 are not failures.

Types#

type Cleanup#

go
type Cleanup struct {
	// contains filtered or unexported fields
}

Cleanup is a stack of resources to release, closed in reverse order of registration. The zero value is ready to use. It is safe for concurrent use.

func (*Cleanup) Add#

go
func (c *Cleanup) Add(name string, fn func(ctx context.Context) error)

Add registers fn to run on Cleanup.Close. The name appears in errors.

func (*Cleanup) AddCloser#

go
func (c *Cleanup) AddCloser(name string, closer io.Closer)

AddCloser registers an io.Closer.

func (*Cleanup) Close#

go
func (c *Cleanup) Close(ctx context.Context) error

Close runs the registered functions in reverse order and removes them. Every function runs even if an earlier one fails; the errors are joined. Calling Close again runs only functions added since the previous call.

type Option#

go
type Option interface {
	// contains filtered or unexported methods
}

An Option configures Run.

func OnShutdown#

go
func OnShutdown(hook func()) Option

OnShutdown adds a hook that runs when shutdown starts, before the drain delay. Use it to fail readiness checks. Hooks run in the order added and must return quickly.

func WithCleanup#

go
func WithCleanup(c *Cleanup) Option

WithCleanup sets the stack Run closes as the last shutdown step.

func WithDrainDelay#

go
func WithDrainDelay(d time.Duration) Option

WithDrainDelay sets how long Run waits after shutdown hooks before cancelling runners. Zero disables the delay. Default: DefaultDrainDelay.

func WithLogger#

go
func WithLogger(logger *slog.Logger) Option

WithLogger sets the logger for lifecycle events. Default: discard.

func WithShutdownTimeout#

go
func WithShutdownTimeout(d time.Duration) Option

WithShutdownTimeout sets how long Run waits for runners to stop, and separately how long the cleanup stack may take. Default: DefaultShutdownTimeout.

func WithSignals#

go
func WithSignals(sigs ...os.Signal) Option

WithSignals sets the signals that start shutdown. With no arguments, Run ignores signals. Default: os.Interrupt and SIGTERM.

type Runner#

go
type Runner interface {
	Run(ctx context.Context) error
}

A Runner is long-running work such as an HTTP server or a job worker.

Run blocks until ctx is done, then stops gracefully and returns nil. Any other return, including returning before ctx is done, is a failure.

type RunnerFunc#

go
type RunnerFunc func(ctx context.Context) error

RunnerFunc adapts a function to the Runner interface.

func (RunnerFunc) Run#

go
func (f RunnerFunc) Run(ctx context.Context) error

Run calls f(ctx).

v0.4
esc
↑↓ move↵ openesc close