• v0.9.0 f2e3faa1d6

    Rene Nochebuena released this 2026-03-18 17:50:05 -06:00 | 1 commits to main since this release

    v0.9.0

    code.nochebuena.dev/go/launcher

    Overview

    launcher orchestrates the startup and shutdown of all infrastructure components in a Go service — database pools, HTTP servers, background workers, and anything else that needs a managed lifecycle. It enforces a strict three-phase order: OnInit (open connections) → BeforeStart hooks (dependency injection wiring) → OnStart (start goroutines and listeners) → wait for OS signal or programmatic trigger → OnStop in reverse registration order. Components are never started before their dependencies are ready and never stopped before the components that depend on them.

    What's Included

    • Component interface — OnInit() error, OnStart() error, OnStop() error
    • Hook type — func() error for BeforeStart wiring functions
    • Options struct — ComponentStopTimeout time.Duration (default 15 s); zero value is valid
    • Launcher interface — Append(components ...Component), BeforeStart(hooks ...Hook), Run() error, Shutdown(ctx context.Context) error
    • New(logger logz.Logger, opts ...Options) Launcher — constructor; no package-level singletons or global state
    • OS signal handling for SIGINT and SIGTERM built into Run
    • Reverse-order shutdown — components stopped in reverse registration order, each with an independent per-component timeout
    • Shutdown(ctx) — idempotent programmatic shutdown; safe to call from multiple goroutines

    Installation

    require code.nochebuena.dev/go/launcher v0.9.0
    

    Design Highlights

    • The three-phase lifecycle separates resource allocation (OnInit), dependency wiring (BeforeStart), and service activation (OnStart), ensuring no component starts serving traffic before its dependencies are ready (see docs/adr/ADR-001-three-phase-lifecycle.md).
    • Shutdown runs in reverse registration order with a per-component independent timeout, so a stalled component does not block others from stopping cleanly (see docs/adr/ADR-002-reverse-order-shutdown.md).
    • BeforeStart hooks are the designated place for dependency injection wiring — they run after all OnInit calls complete but before any OnStart call, giving access to fully initialized (but not yet started) components (see docs/adr/ADR-003-before-start-hooks.md).
    • Shutdown uses a sync.Once to close the trigger channel, making it safe to call from multiple goroutines (e.g. a test teardown and an OS signal handler racing).

    Known Limitations & Edge Cases

    • There is no dependency graph between components. Registration order is the contract — callers must register dependencies before dependents and rely on reverse-order shutdown being the correct inverse. There is no cycle detection or topological sort.
    • OnInit calls are sequential and not parallelised. Services with many slow-initializing components (e.g. large connection pools with health-check warmup) will experience additive startup latency.
    • If OnStart fails for any component, stopAll is called immediately, but only the components whose OnInit succeeded will have OnStop called — components that never started will still receive OnStop. Applications should ensure OnStop is safe to call even if OnStart was never reached.
    • ComponentStopTimeout is per-component. A worst-case shutdown takes n * ComponentStopTimeout where n is the number of components, not a single global deadline.
    • Append and BeforeStart are not safe to call concurrently with Run. All registration must complete before Run is invoked.
    • Shutdown(ctx) context controls the caller-side wait timeout only — it does not override ComponentStopTimeout for individual components.

    v0.9.0 → v1.0.0 Roadmap

    • Evaluate whether parallel OnInit is needed for services with large numbers of slow-initializing components; if so, design the concurrency model carefully to preserve clear error attribution.
    • Validate the reverse-order shutdown guarantee under production load, particularly with HTTP servers that need to drain in-flight requests before closing DB connections.
    • Consider providing a global Shutdown deadline option (in addition to per-component ComponentStopTimeout) for environments where total shutdown time is bounded by an orchestrator.
    • Document and test the exact behaviour when OnStop is called on a component whose OnStart was never reached.
    • Achieve production validation of Shutdown idempotency under concurrent signal + programmatic teardown racing.

    v0.9.0 rationale: The API is stable and intentional — designed through multiple architecture reviews and tested end-to-end via the todo-api POC (SQLite, RBAC, middleware stack, HTTP handlers). The module is not yet battle-tested in production for all edge cases, and the pre-1.0 designation preserves the option for minor API refinements based on real-world use.

    Downloads