C.W.K.
Stream
Lesson 05 of 07 · published

Graceful Shutdown

~11 min · management, lifespan, shutdown

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

What happens when uvicorn restarts

If you do nothing, every WebSocket connection ends with code 1006 (abnormal closure) on restart. Clients see a network error; reconnection logic kicks in; everyone is fine. But you can do better: send a system.reconnect message giving the client a heads-up, then close cleanly with 1001 (going away).

FastAPI lifespan

Use the lifespan context manager. Whatever runs after yield is shutdown. Close every active WebSocket, then return — uvicorn will not exit until lifespan returns.

Concurrent close with gather

Closing 10,000 connections one at a time takes minutes. Use asyncio.gather(*close_tasks, return_exceptions=True) to fan out and wait for all of them concurrently. return_exceptions=True ensures one bad client does not abort the rest.

Code

Lifespan-managed shutdown·python
from contextlib import asynccontextmanager
from fastapi import FastAPI
import asyncio

@asynccontextmanager
async def lifespan(app: FastAPI):
    yield  # serve

    # Shutdown phase
    log.info('draining %d WebSocket connections', manager.total_connections)
    # 1. Tell clients we're going.
    for room in list(manager.rooms):
        await manager.broadcast(room, {
            'type': 'system.reconnect',
            'data': {'reason': 'server-restart'},
        })

    # 2. Brief grace period so clients receive the message.
    await asyncio.sleep(1.0)

    # 3. Close all connections concurrently.
    close_tasks = [
        ws.close(code=1001, reason='server-restart')
        for ws in list(manager.ws_meta)
    ]
    await asyncio.gather(*close_tasks, return_exceptions=True)
    log.info('all WebSocket connections drained')

app = FastAPI(lifespan=lifespan)

External links

Exercise

Add the lifespan above to your server. Send SIGTERM (e.g. kill -TERM <pid>); on the client, you should see a system.reconnect message followed by a clean 1001 close. Without the lifespan, you would see 1006 cold.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.