Runtime Service Lifecycle Guide¶
Trussium provides a small public lifecycle contract for application-scoped runtime services. It defines deterministic asynchronous startup, reverse-order shutdown, partial-startup rollback, bounded cleanup, and safe operational failure reporting. The runtime service registry now provides the separate registration and lookup layer that composes this lifecycle contract.
Service contract¶
Implement RuntimeService with a stable name and asynchronous startup()
and shutdown() hooks:
from trussium.app import create_application
class CacheService:
name = "cache"
async def startup(self) -> None:
await self.connect()
async def shutdown(self) -> None:
await self.disconnect()
cache = CacheService()
application = create_application(runtime_services=(cache,))
Service names must match [a-z][a-z0-9_.-]{0,63} and must be unique within
one lifecycle plan. Names are operational identifiers, so they must never
contain credentials, endpoints, tenant data, request data, or other secrets.
The application factory registers the supplied sequence, seals the resolved
registry, and builds the lifecycle from its immutable ordered snapshot. It
exposes the registry as application.state.runtime_service_registry and its
coordinator as application.state.runtime_service_lifecycle. Existing factory
calls that do not supply services remain compatible. Callers that need lookup
or discovery can explicitly compose a RuntimeServiceRegistry; see the
Runtime Service Registry Guide.
Ordering¶
Startup follows declaration order. Shutdown follows the reverse order:
runtime.started is emitted only after every configured service starts. The
production server has already drained active JSON and SSE work before FastAPI
runs application shutdown. Runtime-service hooks then run before the existing
readiness-client and tracing-exporter cleanup, preserving their established
relative order.
Hooks are sequential by design. The lifecycle layer does not infer service dependencies, retry hooks, or run hooks concurrently.
Partial-startup rollback¶
If a startup hook fails, later services are not started. Only services whose startup hooks completed are rolled back, in reverse order:
Rollback continues when one cleanup hook fails or times out. The original
startup failure and bounded rollback failures are then available through one
RuntimeServiceLifecycleError.
Bounded cleanup¶
Every service shutdown or rollback hook gets its own cleanup deadline. The default is 10 seconds:
The value must be finite and greater than zero. A timeout cancels that hook, records a stable timeout failure, and proceeds to the next eligible service. This deadline is separate from provider execution timeouts and the server's active-request drain deadline.
States and failures¶
RuntimeServiceLifecycle exposes these deterministic states:
Startup and shutdown may each run only once. Repeated or out-of-order calls
raise RuntimeServiceStateError without re-running hooks.
After every eligible cleanup hook has run, operational failures raise
RuntimeServiceLifecycleError. Both concrete errors inherit the public
LifecycleError, RuntimeExecutionError, TrussiumError, and RuntimeError
catch boundaries. The aggregate exposes:
phase:startup,rollback, orshutdown.failures: an immutable tuple ofRuntimeServiceFailurevalues.code: a stable aggregate code such asruntime_service_shutdown_failed.message: a bounded count without raw exception text.
Each failure value contains only service_name, phase, code, and
error_type. Original startup exceptions remain available as exception causes
for internal diagnosis, but their messages are not copied into the public
error or operational log contract.
asyncio.CancelledError is never converted into a Trussium error. Trussium
still attempts eligible bounded rollback or shutdown hooks before propagating
the native cancellation.
Operational events¶
Each hook emits ordered structured events:
| Phase | Events |
|---|---|
| Startup | runtime.service.startup.started, .completed, .failed, .cancelled |
| Rollback | runtime.service.rollback.started, .completed, .failed, .timeout, .cancelled |
| Shutdown | runtime.service.shutdown.started, .completed, .failed, .timeout, .cancelled |
Events may include runtime_service, lifecycle_phase, duration_ms,
cleanup_timeout_seconds, error_code, error_type, and outcome. They do
not include exception messages, tracebacks, service objects, hook return
values, credentials, endpoints, or request payloads.
Extension boundary¶
Use lifecycle hooks only for resources whose ownership matches the application lifespan. Hooks should be cooperative, idempotent at the resource boundary, and should release partially initialized internal resources before raising when possible.
Service registration and lookup remain a separate sealed registry layer. Neither layer provides dependency ordering, dependency injection, automatic discovery, or plugins. Optional component health reporting is a separate read-only contract over the sealed registry and never invokes lifecycle hooks; see Runtime Component Health Reporting.