encode / uvicorn

An ASGI web server, for Python. šŸ¦„
https://www.uvicorn.org/
BSD 3-Clause "New" or "Revised" License
8.11k stars 701 forks source link

fix: stop server on "lifespan.shutdown.failed" #2309

Closed peterschutt closed 1 month ago

peterschutt commented 2 months ago

Summary

Stop the server if a "lifespan.shutdown.failure" message is received from the application after "lifespan.startup.complete" has been sent by the application, but before the server has sent "lifespan.shutdown".

Applications typically use a context manager around the asgi lifespan protocol to manage resources that should share the same lifecycle as the web server. Where a managed resource encounters an error before the server has initiated shutdown, app frameworks such as starlette and litestar will send a lifespan.shutdown.failed message.

E.g, in starlette:

    async def lifespan(self, scope: Scope, receive: Receive, send: Send) -> None:
        started = False
        app: typing.Any = scope.get("app")
        await receive()
        try:
            async with self.lifespan_context(app) as maybe_state:
                ...
        except BaseException:
            exc_text = traceback.format_exc()
            if started:
                await send({"type": "lifespan.shutdown.failed", "message": exc_text})
            else:
                await send({"type": "lifespan.startup.failed", "message": exc_text})
            raise
        else:
            await send({"type": "lifespan.shutdown.complete"})

This PR gives the application a way to tell the server that something critical has failed internally, and that it should shut down.

Closes #2308

Checklist

Kludex commented 1 month ago

I've explained on the issue why this is not an issue.