lilydocs
HTTP APIWebSocketConsumer
Running the application4 min read

Graceful shutdown

Stop new admission, finish admitted work, and close resources after collecting evidence of actual termination.

Graceful shutdown does more than close the listener. Lily stops new connection and message admission, tracks admitted work and cleanup, and attempts to verify that work using the resources has terminated.

Initiate shutdown#

start().await listens for platform signals. start_with_cancellation(token) connects the enclosing application's signal to the same flow. Another owner can call app.close().await. Concurrent/repeated close calls for one app observe the same result; each call does not start a new timeout.

For this helper, add tokio-util = "0.7" to [dependencies] in Cargo.toml.

Function for the parent application
use lilyrs::websocket::WsApp;
use tokio_util::sync::CancellationToken;

async fn serve(app: WsApp, stop: CancellationToken)
    -> Result<(), Box<dyn std::error::Error>>
{
    app.start_with_cancellation(stop).await?;
    Ok(())
}

Cancelling the caller's clone of stop initiates shutdown. An app that was never started must also be closed. Dropping only the app value without awaiting the async result does not mean cleanup completed.

Normal drain order#

Shutdown flow
Stop accepting new Upgrades and messages
  → discard pending inbound application messages
  → allow accepted actions and scope cleanup to run
  → drain accepted terminal frames
  → 1001 Going Away / lily.v2.server_shutdown
  → bounded wait for peer Close and transport shutdown
  → evidence of hook, task and scope termination
  → app-owned backplane, DI and telemetry resources

Normal draining does not immediately cancel a selected action merely because shutdown began. New Text/Binary messages are not dispatched; protocol traffic can still be read. During shutdown, the write timeout is a shared cap for terminal draining, Close, and the peer's response.

Execution and send authority#

Admitted execution and cleanup callbacks may attempt awaited sends with their active invocation authority. A previously retained context/proxy or raw spawned task does not inherit that callback's drain authority.

A cancellation notification does not immediately remove sending during the cooperative window. Execution cutoff or invocation completion ends that authority. Deadlines explains the distinction between the signal, cooperation, and cleanup.

Interpret the result#

Event Establishes Does not establish on its own
Cancellation requested A termination request was signalled Task join, SQL rollback
Action returned An actual normal result exists DI closure or peer delivery
Frame queued Local queue acceptance Write/flush or client processing
Scope ended A cleanup result for that scope exists Every app task has ended
App close succeeded Required framework shutdown work was verified Every past message was delivered

A timeout is not reported as cleanup success. A close error may mean user cleanup failed or that actual child termination could not be verified. An external container owner must not unconditionally close the container without distinguishing these cases; the application example closes it after app success.

Shared budget and external work#

Graceful, forced, and final reconciliation proceed within the same absolute root budget; phases do not each start a fresh duration. Configure the upper bound through [lifecycle].shutdown_timeout_secs. Observe shutdown diagnostics through tracing; there is no public shutdown-report accessor.

A blocking user future poll/destructor or a task started outside the framework cannot be stopped by magic. Lily cancels workers registered with add_background_service::<T>() within the shared budget and awaits actual termination before scope and owned DI cleanup proceed. Workers receive BackgroundCancellation, not message ExecutionCancellation. Your application still owns stop/join for raw tasks; worker registration does not grant new WebSocket send authority during shutdown.

Sources for this page v0.1.0

This guide is based on the reviewed framework commit.

QUICK START
Documentation
Source code