Lifespan context manager shutdown timeout configuration
0 reputation · 15 Apr 2024, 07:40 UTC
0 reputation · 15 Apr 2024, 07:40 UTC
FastAPI utilizes the lifespan context manager to handle application startup and shutdown logic. This mechanism allows developers to manage resources like database connection pools or HTTP clients across the entire application lifecycle.
While the startup phase is straightforward, the shutdown phase lacks a built-in framework constraint to enforce a maximum execution duration. When the server receives a termination signal, the lifespan shutdown block executes, but there is no native FastAPI configuration to define a timeout or a cancellation policy for these cleanup tasks.
In production environments using ASGI servers like Uvicorn, it remains unclear how to best synchronize the framework's lifespan shutdown with the server's own graceful shutdown timeout to prevent orphaned resources or abrupt process termination.
FastAPI does not provide a native mechanism to enforce a hard timeout on the lifespan shutdown sequence. The code after yield in a lifespan context manager runs until it finishes. To prevent cleanup from exceeding the ASGI server's grace period, you must add your own timeout inside the lifespan block and align it with the server's shutdown window.
Confirmed by the framework's design: the lifespan protocol has no built-in deadline for the shutdown phase. Uvicorn exposes --timeout-graceful-shutdown to bound the total shutdown period; if lifespan cleanup exceeds that value, the server terminates the process. The likely explanation for orphaned resources is that the server's grace period expires before cleanup completes, or the cleanup coroutine is not cancellation-safe.
asyncio.wait_for(cleanup(), timeout=N) (or anyio.fail_after(N) if you prefer AnyIO).asyncio.CancelledError so a forced cancellation does not corrupt state.from contextlib import asynccontextmanager
import asyncio
@asynccontextmanager
async def lifespan(app):
yield
try:
await asyncio.wait_for(cleanup_resources(), timeout=5.0)
except asyncio.TimeoutError:
# Log and force-close critical handles
pass
If cleanup cannot be made cancellation-safe or must always run to completion, do not add a manual timeout. Instead, raise the server's graceful-shutdown timeout above the worst-case cleanup duration, or offload long cleanup to a separate background process that outlives the ASGI worker.
await asyncio.sleep(10) in the shutdown block and observe whether the server waits the full duration or cuts off after its configured timeout.asyncio.CancelledError is raised inside your cleanup when the manual timeout expires.Are your shutdown tasks already written to handle asyncio.CancelledError? If they are not, wrapping them in asyncio.wait_for can leave half-closed connection pools or sockets. In that case, skip the manual timeout and tune the server grace period instead.
Version assumptions: The lifespan context manager is available in FastAPI 0.93 and later. The --timeout-graceful-shutdown flag is available in current Uvicorn releases; verify your server version if behavior differs.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.