Files
launcher/docs/adr/ADR-002-reverse-order-shutdown.md

49 lines
2.1 KiB
Markdown
Raw Permalink Normal View History

# ADR-002: Reverse-Order Shutdown
**Status:** Accepted
**Date:** 2026-03-18
## Context
When stopping an application, components must be stopped in the reverse of the order
they were started. A component that depends on another (e.g. an HTTP server that
uses a database connection pool) must be stopped before the component it depends on.
If the database pool were closed first, any in-flight request handled by the HTTP
server would fail with a connection error rather than completing gracefully.
## Decision
`stopAll` iterates `l.components` from the last index to the first:
```go
for i := len(l.components) - 1; i >= 0; i-- {
// stop l.components[i]
}
```
Components are typically registered in dependency order (dependencies first, then
dependents). Reverse-order shutdown therefore stops dependents before dependencies.
Each component's `OnStop` runs in its own goroutine. A `time.After` ticker enforces
the per-component `ComponentStopTimeout` (default 15 seconds). If a component does
not return within the timeout, the launcher logs an error and moves on to the next
component — it does not block the remainder of the shutdown sequence.
`Shutdown(ctx context.Context) error` provides a caller-side wait: it closes
`shutdownCh` (idempotent via `sync.Once`) and then blocks until `doneCh` is closed
(when `Run` returns) or until `ctx` is done. The caller's context controls how long
the caller is willing to wait for the entire shutdown; `ComponentStopTimeout`
controls how long each individual component gets.
## Consequences
- Shutdown order mirrors startup order in reverse, which is correct for any DAG of
component dependencies without requiring an explicit dependency graph.
- Each component's timeout is independent — a single slow component does not block
others from stopping.
- `Shutdown` is idempotent: calling it multiple times from multiple goroutines (e.g.
an OS signal handler and a health-check endpoint) is safe.
- If `ComponentStopTimeout` is exceeded, the component is abandoned — resources may
not be fully released. This is the correct trade-off for a graceful shutdown with
a deadline; the alternative (waiting indefinitely) is worse.