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 ¶
const ( DefaultDrainDelay = 5 * time.Second DefaultShutdownTimeout = 25 * time.Second )
Defaults used by Run.
Variables ¶
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") )
Functions ¶
func Run ¶
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:
- Shutdown hooks run (for example, marking the service not ready).
- The drain delay elapses, giving load balancers time to stop routing. It is skipped when shutdown started because a runner stopped.
- The runners' context is cancelled; Run waits up to the shutdown timeout.
- 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 ¶
Add registers fn to run on Cleanup.Close. The name appears in errors.
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 ¶
WithCleanup sets the stack Run closes as the last shutdown step.
func WithDrainDelay ¶
WithDrainDelay sets how long Run waits after shutdown hooks before cancelling runners. Zero disables the delay. Default: DefaultDrainDelay.
func WithLogger ¶
WithLogger sets the logger for lifecycle events. Default: discard.
func WithShutdownTimeout ¶
WithShutdownTimeout sets how long Run waits for runners to stop, and separately how long the cleanup stack may take. Default: DefaultShutdownTimeout.
func WithSignals ¶
WithSignals sets the signals that start shutdown. With no arguments, Run ignores signals. Default: os.Interrupt and SIGTERM.