Help us shape the future of Go! Take the 2026 Go Developer Survey

app

package
v0.3.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 23, 2026 License: Apache-2.0 Imports: 10 Imported by: 1

Documentation

Overview

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: stable (ADR-0015, ADR-0054). Design: ADR-0017.

Index

Examples

Constants

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

Defaults used by Run.

Variables

View Source
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

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.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"gorbital.dev/app"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	time.AfterFunc(10*time.Millisecond, cancel) // stands in for SIGTERM

	cleanup := &app.Cleanup{}
	cleanup.Add("database", func(context.Context) error {
		fmt.Println("database closed")
		return nil
	})

	worker := app.RunnerFunc(func(ctx context.Context) error {
		fmt.Println("worker started")
		<-ctx.Done()
		fmt.Println("worker stopped")
		return nil
	})

	err := app.Run(ctx, []app.Runner{worker},
		app.WithSignals(),
		app.WithDrainDelay(0),
		app.WithCleanup(cleanup),
	)
	fmt.Println("error:", err)
}
Output:
worker started
worker stopped
database closed
error: <nil>

Types

type Cleanup

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

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

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

AddCloser registers an io.Closer.

func (*Cleanup) Close

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

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

An Option configures Run.

func OnShutdown

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

func WithCleanup(c *Cleanup) Option

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

func WithDrainDelay

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

func WithLogger(logger *slog.Logger) Option

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

func WithShutdownTimeout

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

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

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

type RunnerFunc func(ctx context.Context) error

RunnerFunc adapts a function to the Runner interface.

func (RunnerFunc) Run

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

Run calls f(ctx).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL