feat(app): component registry with ordered start/shutdown
What does this MR do and why?
Rust services on labkit-rs currently hand-write their own startup and shutdown
boilerplate. This adds an opt-in app feature that gives them a small lifecycle
runtime instead, matching what Go services already get from Go labkit v2's
app.App.
A service describes each of its subsystems — a server, a background worker, a client — as a component and registers it with the app. The app brings components up in the order they were registered and takes them down in the reverse order, so a component can rely on the ones before it being up while it starts, and being torn down only after it has stopped. If a component fails to start, the ones already started are shut down again so the process doesn't come up half-alive.
This is the first step of the lifecycle work. It deliberately stops at ordered start/shutdown; OS signal handling, non-critical components with retry, and wiring the app into the shared logging/tracing/metrics runtime follow in the MRs stacked on top of this one.
Part of #39 (closed)
Related Issues
Closes part of #39 (closed)
Testing
Unit tests cover reverse-order shutdown, a critical start failure unwinding the components already started, and shutdown being a no-op before start. The doc example is compiled as a doctest.