Documentation
¶
Overview ¶
Package lifecycle provides a centralized library for managing application lifecycles and interactive I/O.
Dual Signal Context ¶
Standard Go `signal.NotifyContext` cancels on the first signal. `lifecycle` distinguishes between:
- SIGINT (Ctrl+C): "Soft" interrupt. Captures the signal but keeps the Context active. Allows the application to decide whether to pause, confirm exit, or ignore.
- SIGTERM: "Hard" stop. Cancels the Context immediately, triggering graceful shutdown.
Interruptible I/O ¶
On many systems (especially Windows), reading from `os.Stdin` blocks the goroutine indefinitely, preventing clean cancellation. Furthermore, on Windows, receiving a signal can close the standard input handle, causing an unexpected EOF. `lifecycle` provides `OpenTerminal` (using `CONIN$`) and `NewInterruptibleReader` to ensure I/O operations respect `context.Context` cancellation and signals are handled gracefully without premature termination.
Shutdown Timeouts ¶
Graceful shutdown often involves waiting for background goroutines to finish (e.g., closing database connections, flushing logs). To prevent the application from hanging indefinitely if a cleanup operation stalls, `lifecycle` provides `BlockWithTimeout`. This ensures the process exits deterministically even if some components are stuck.
Reliability Primitives (v1.4) ¶
For "Durable Execution" patterns, `lifecycle` provides `Do(ctx, fn)`. This creates a "Critical Section" where value-critical operations (like state commits) are shielded from cancellation. If a user hits Ctrl+C during a critical section, the signal is captured but deferred until the section completes.
Worker Protocol & Supervision (v1.3) ¶
For complex applications, `lifecycle` provides a `Worker` interface and a `Supervisor`. This allows managing hierarchies of processes, goroutines, and containers with automatic restarts, session persistence (Handover Protocol), and unified introspection.
DX Helpers & Boilerplate (v1.4) ¶
To reduce friction, `lifecycle` provides helpers that standardizes common patterns:
- Run: Standardizes the `main` function (Context creation -> Run -> Stop -> Wait).
- Sleep: Replaces `select { case <-time.After... }` with a single context-aware call.
- OnShutdown: Registers hooks without manual type assertions.
Usage ¶
func main() {
err := lifecycle.Run(runApp)
if err != nil {
// handle err
}
}
func runApp(ctx context.Context) error {
// Register cleanup
lifecycle.OnShutdown(ctx, func() {
fmt.Println("Closing DB...")
})
// Safe Sleep (Regret Window)
if err := lifecycle.Sleep(ctx, 5*time.Second); err != nil {
return err
}
return nil
}
See examples/hooks for a full application structure.
Index ¶
- Constants
- Variables
- func BlockWithTimeout(done <-chan struct{}, timeout time.Duration) error
- func Do(parent context.Context, fn func(ctx context.Context)) error
- func IsInterrupted(err error) bool
- func NewInterruptibleReader(base io.Reader, cancel <-chan struct{}) *termio.InterruptibleReader
- func NewLogMetricsProvider() metrics.Provider
- func NewSignalContext(parent context.Context, opts ...signal.Option) *signal.Context
- func OnShutdown(ctx context.Context, fn func())
- func OpenTerminal() (io.ReadCloser, error)
- func Run(fn func(context.Context) error, opts ...Option) error
- func SetLogger(l *slog.Logger)
- func SetMetricsProvider(p metrics.Provider)
- func SetStrictMode(strict bool)
- func SignalStateDiagram(s SignalState) string
- func Sleep(ctx context.Context, d time.Duration) error
- func StartProcess(cmd *exec.Cmd) error
- func SystemDiagram(sig SignalState, work WorkerState) string
- func UpgradeTerminal(r io.Reader) (io.Reader, error)
- func WithForceExit(threshold int) signal.Option
- func WithHookTimeout(d time.Duration) signal.Option
- func WithInterrupt(cancel bool) signal.Option
- func WorkerStateDiagram(s WorkerState) string
- func WorkerTreeDiagram(s WorkerState) string
- type Container
- type ContainerStatus
- type Context
- type Option
- type SignalState
- type State
- type Supervisor
- type SupervisorBackoff
- type SupervisorFactory
- type SupervisorSpec
- type SupervisorStrategy
- type Worker
- type WorkerState
- type WorkerStatus
Examples ¶
Constants ¶
const ( WorkerStatusPending = worker.StatusPending WorkerStatusRunning = worker.StatusRunning WorkerStatusStopped = worker.StatusStopped WorkerStatusFailed = worker.StatusFailed )
const ( // EnvResumeID is the unique session identifier for a worker. EnvResumeID = worker.EnvResumeID // EnvPrevExit is the exit code of the previous execution of this worker. EnvPrevExit = worker.EnvPrevExit )
Handover Constants
const ( // StrategyOneForOne: If a child process terminates, only that process is restarted. StrategyOneForOne = supervisor.StrategyOneForOne // StrategyOneForAll: If a child process terminates, all other child processes are terminated. StrategyOneForAll = supervisor.StrategyOneForAll )
Variables ¶
var Version string
Functions ¶
func BlockWithTimeout ¶ added in v1.1.0
BlockWithTimeout blocks until the done channel is closed or the timeout expires. Alias for pkg/runtime.BlockWithTimeout.
Example ¶
ExampleBlockWithTimeout demonstrates how to enforce a deadline on shutdown cleanup.
package main
import (
"fmt"
"time"
"github.com/aretw0/lifecycle"
)
func main() {
done := make(chan struct{})
// Simulate a cleanup task
go func() {
defer close(done)
// Simulate fast cleanup
time.Sleep(10 * time.Millisecond)
}()
// Wait for cleanup, but give up after 1 second
err := lifecycle.BlockWithTimeout(done, 1*time.Second)
if err != nil {
fmt.Println("Cleanup timed out!")
} else {
fmt.Println("Cleanup finished successfully")
}
}
Output: Cleanup finished successfully
func Do ¶ added in v1.4.0
Do executes a function in a "Critical Section" that delays context cancellation. It wraps the provided function in a shielded context that ignores the parent's cancellation. Alias for internal/reliability.Do.
func IsInterrupted ¶
IsInterrupted checks if an error indicates an interruption (Context Canceled, EOF, etc.). Alias for pkg/termio.IsInterrupted.
func NewInterruptibleReader ¶
func NewInterruptibleReader(base io.Reader, cancel <-chan struct{}) *termio.InterruptibleReader
NewInterruptibleReader returns a reader that checks the cancel channel before/after blocking reads. Alias for pkg/termio.NewInterruptibleReader.
func NewLogMetricsProvider ¶ added in v1.1.0
NewLogMetricsProvider returns a metrics provider that logs to the current logger. Useful for development and local verification. Alias for pkg/metrics.LogProvider.
func NewSignalContext ¶
NewSignalContext creates a context that cancels on SIGTERM/SIGINT. On the first signal, context is cancelled. On the second, it force exits. Behavior can be customized via functional options. Alias for pkg/signal.NewContext.
Example ¶
ExampleNewSignalContext demonstrates how to use the Dual Signal context. Note: This example is illustrative; in a real run, it waits for SIGINT/SIGTERM.
package main
import (
"context"
"fmt"
"time"
"github.com/aretw0/lifecycle"
)
func main() {
// Create a context that listens for signals.
ctx := lifecycle.NewSignalContext(context.Background())
// For checking output deterministically in this example, we cancel manually
// after a short delay, allowing "work" to happen first.
go func() {
time.Sleep(50 * time.Millisecond)
ctx.Cancel()
}()
// Simulate work
select {
case <-ctx.Done():
fmt.Println("Context cancelled too early")
case <-time.After(10 * time.Millisecond):
fmt.Println("Doing work...")
}
}
Output: Doing work...
func OnShutdown ¶ added in v1.4.0
OnShutdown safely registers a shutdown hook on the context if it supports it. It abstracts the type assertion for *signal.Context.
func OpenTerminal ¶
func OpenTerminal() (io.ReadCloser, error)
OpenTerminal checks for text input capability and returns a Reader. On Windows, it tries to open CONIN$. Alias for pkg/termio.Open.
Example ¶
ExampleOpenTerminal demonstrates how to open the terminal safely.
package main
import (
"fmt"
"github.com/aretw0/lifecycle"
)
func main() {
// OpenTerminal handles OS-specific logic (like CONIN$ on Windows)
reader, err := lifecycle.OpenTerminal()
if err != nil {
fmt.Printf("Error opening terminal: %v\n", err)
return
}
defer reader.Close()
fmt.Println("Terminal opened successfully")
// Wrap with InterruptibleReader to respect context cancellation
// r := lifecycle.NewInterruptibleReader(reader, ctx.Done())
}
Output: Terminal opened successfully
func Run ¶ added in v1.4.0
Run executes the application logic with a managed SignalContext. Alias for pkg/runtime.Run.
func SetLogger ¶ added in v1.1.0
SetLogger overrides the global logger used by the library. Alias for pkg/log.SetLogger.
func SetMetricsProvider ¶ added in v1.1.0
SetMetricsProvider overrides the global metrics provider. This allowing bridging library metrics to Prometheus, OTEL, etc. Alias for pkg/metrics.SetProvider.
func SetStrictMode ¶ added in v1.1.0
func SetStrictMode(strict bool)
SetStrictMode sets whether to block on unsupported platforms for process hygiene. Alias for pkg/proc.StrictMode.
func SignalStateDiagram ¶ added in v1.3.0
func SignalStateDiagram(s SignalState) string
SignalStateDiagram returns a Mermaid state diagram string representing the signal context configuration. Alias for pkg/signal.MermaidState.
func Sleep ¶ added in v1.4.0
Sleep pauses the current goroutine for at least the duration d. Alias for pkg/runtime.Sleep.
func StartProcess ¶ added in v1.1.0
StartProcess starts the specified command with process hygiene (auto-kill on parent exit). Alias for pkg/proc.Start.
func SystemDiagram ¶ added in v1.3.0
func SystemDiagram(sig SignalState, work WorkerState) string
SystemDiagram returns a unified Mermaid diagram representing the entire application lifecycle. It combines the SignalContext (Control Plane) and the Worker hierarchy (Data Plane).
func UpgradeTerminal ¶ added in v0.1.1
UpgradeTerminal checks if the provided reader is a terminal and returns a safe reader (e.g. CONIN$ on Windows). If not a terminal, returns the original reader.
func WithForceExit ¶ added in v1.1.0
WithForceExit configures the threshold of signals required to trigger an immediate os.Exit(1). Set to 0 to disable forced exit. Alias for pkg/signal.WithForceExit.
func WithHookTimeout ¶ added in v1.2.0
WithHookTimeout configures the duration after which a running hook produces a warning log. Alias for pkg/signal.WithHookTimeout.
func WithInterrupt ¶ added in v1.1.0
WithInterrupt configures whether SIGINT (Ctrl+C) should cancel the context. Alias for pkg/signal.WithInterrupt.
func WorkerStateDiagram ¶ added in v1.3.0
func WorkerStateDiagram(s WorkerState) string
WorkerStateDiagram returns a Mermaid state diagram string representing the worker state transitions. Alias for pkg/worker.MermaidState.
func WorkerTreeDiagram ¶ added in v1.3.0
func WorkerTreeDiagram(s WorkerState) string
WorkerTreeDiagram returns a Mermaid diagram string representing the worker structure (Tree). Alias for pkg/worker.MermaidTree.
Types ¶
type Container ¶ added in v1.3.0
Container represents a generic container interface. Alias for container.Container.
func NewMockContainer ¶ added in v1.3.0
NewMockContainer creates a new MockContainer for testing. Alias for pkg/container.NewMockContainer.
type ContainerStatus ¶ added in v1.3.0
ContainerStatus represents the lifecycle state of a container. Alias for container.Status.
type SignalState ¶ added in v1.3.0
SignalState represents the configuration state of the SignalContext.
type State ¶ added in v1.2.0
type State = SignalState
State is an alias for SignalState (backward compatibility).
type Supervisor ¶ added in v1.3.0
type Supervisor = supervisor.Supervisor
Supervisor defines the interface for a supervisor. Alias for pkg/supervisor.Supervisor.
func NewSupervisor ¶ added in v1.3.0
func NewSupervisor(name string, strategy SupervisorStrategy, specs ...SupervisorSpec) Supervisor
NewSupervisor creates a new Supervisor for the given workers. Alias for pkg/supervisor.New.
type SupervisorBackoff ¶ added in v1.3.1
type SupervisorBackoff = supervisor.Backoff
SupervisorBackoff defines the retry policy for failed children. Alias for pkg/supervisor.Backoff.
type SupervisorFactory ¶ added in v1.3.0
type SupervisorFactory = supervisor.Factory
SupervisorFactory is a function that creates a new worker instance. Alias for pkg/supervisor.Factory.
type SupervisorSpec ¶ added in v1.3.0
type SupervisorSpec = supervisor.Spec
SupervisorSpec defines the configuration for a supervised child worker. Alias for pkg/supervisor.Spec.
type SupervisorStrategy ¶ added in v1.3.0
type SupervisorStrategy = supervisor.Strategy
SupervisorStrategy defines how the supervisor handles child failures. Alias for pkg/supervisor.Strategy.
type Worker ¶ added in v1.3.0
Worker defines the interface for a managed unit of work. Alias for pkg/worker.Worker.
func NewContainerWorker ¶ added in v1.3.0
NewContainerWorker creates a new Worker from a Container interface. Alias for pkg/worker.NewContainerWorker.
func NewProcessWorker ¶ added in v1.3.0
NewProcessWorker creates a new Process worker for the given command. Alias for pkg/worker.NewProcessWorker.
type WorkerState ¶ added in v1.3.0
WorkerState represents the snapshot of a worker's state.
type WorkerStatus ¶ added in v1.3.0
WorkerStatus represents the lifecycle state of a worker.
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
container
command
|
|
|
demo
command
|
|
|
hooks
command
|
|
|
interactive_dx
command
|
|
|
introspection
command
|
|
|
observability
command
|
|
|
reliability
command
|
|
|
supervisor
command
|
|
|
supervisor/dynamic
command
|
|
|
worker
command
|
|
|
zombie
command
|
|
|
internal
|
|
|
pkg
|
|
|
container
Package container defines a generic interface for managing containerized workloads.
|
Package container defines a generic interface for managing containerized workloads. |
|
log
Package log provides a lightweight, structured logging interface for the lifecycle library.
|
Package log provides a lightweight, structured logging interface for the lifecycle library. |
|
metrics
Package metrics provides a decoupled interface for collecting library metrics.
|
Package metrics provides a decoupled interface for collecting library metrics. |
|
proc
Package proc provides primitives for managing process lifecycle and hygiene.
|
Package proc provides primitives for managing process lifecycle and hygiene. |
|
runtime
Package runtime provides utilities for deterministic process management and boilerplate reduction.
|
Package runtime provides utilities for deterministic process management and boilerplate reduction. |
|
signal
Package signal provides a stateful signal context with introspection and LIFO hooks.
|
Package signal provides a stateful signal context with introspection and LIFO hooks. |
|
supervisor
Package supervisor implements the Supervisor Pattern for managing process and worker lifecycles.
|
Package supervisor implements the Supervisor Pattern for managing process and worker lifecycles. |
|
termio
Package termio provides interruptible I/O primitives and terminal handling.
|
Package termio provides interruptible I/O primitives and terminal handling. |
|
worker
Package worker defines interfaces and implementations for managed units of work.
|
Package worker defines interfaces and implementations for managed units of work. |