Conversation
…endencies with yield
…d, as those caused memory issues before
…rated background tasks
…from dependencies AsyncExitStack
Contributor
|
📝 Docs preview for commit 9087a2a at: https://3154752a.fastapitiangolo.pages.dev Modified Pages |
Contributor
|
📝 Docs preview for commit 343d3a2 at: https://01ef1bba.fastapitiangolo.pages.dev Modified Pages |
Contributor
|
📝 Docs preview for commit 7acdb2c at: https://ac42d2b2.fastapitiangolo.pages.dev Modified Pages |
This was referenced Sep 21, 2025
yield, close after the response is doneStreamingResponses with dependencies with yield or UploadFiles, close after the response is done
Contributor
|
📝 Docs preview for commit e999745 at: https://b0ab811e.fastapitiangolo.pages.dev Modified Pages |
Contributor
|
📝 Docs preview for commit b338294 at: https://07fe17cb.fastapitiangolo.pages.dev Modified Pages |
nilslindemann
added a commit
to nilslindemann/fastapi
that referenced
this pull request
Oct 1, 2025
YuriiMotov
added a commit
that referenced
this pull request
Oct 1, 2025
|
This PR also fixes #12508 |
|
But introduces another bug as described here |
5 tasks
eumemic
added a commit
to eumemic/aios
that referenced
this pull request
May 22, 2026
## Summary - **Bug:** The four SSE route handlers open a dedicated asyncpg connection via `open_listen_for_*` BEFORE constructing `EventSourceResponse` (preserves #376 — preflight failures must surface as 503 before 200 OK). The wrapped generator owns `subscription.terminate()` in `finally`. But if the request task is cancelled in FastAPI's dispatch gap — between `response = await handler(request)` and the subsequent `await response(scope, receive, send)` — the async generator is **never started**, and Python's async-generator semantics guarantee that an unstarted generator's `finally` block does NOT execute on `aclose` or GC. The asyncpg connection, its `LISTEN`, and the SSE subscriber advisory lock leak until TCP keepalive reaps the backend (~2h). Server restart with N concurrent in-flight SSE setups leaks up to N postgres backends. - **Reachability:** narrow (server shutdown / fast client disconnect striking the µs-scale dispatch window) but real and persistent. - **Fix:** `make_sse_response(subscription, content, *, ping=15)` in `api/sse.py` constructs the `EventSourceResponse` and registers `weakref.finalize(response, subscription.terminate)`. The finalizer fires on response GC — including the un-invoked path. Migrated the 4 SSE route sites to use the helper. - **Idempotency:** `terminate()` is synchronous (see `db/listen.py` docstring) and idempotent at asyncpg's transport layer, so the duplicate call in the normal path (generator `finally` + finalizer) is a no-op. - **Design alternative considered:** FastAPI `Depends`-with-yield (one mechanism via `AsyncExitStack`). Rejected after code review: (a) `Depends` teardown's cancellation guarantee only stabilized in [fastapi#14099](fastapi/fastapi#14099) (Sep 2024), vs `weakref.finalize`'s stdlib-since-3.4 stability; (b) the refactor would scatter `SSE_PREFLIGHT_EXCEPTIONS` handling and per-stream `detail` payloads across 4 dependency functions; (c) the two cleanup hooks here operate on **disjoint object-graph states** (started-then-disconnect vs never-started), not duplicating a single guard. ## Test plan - [x] mypy — clean (160 source files) - [x] ruff check + format — clean - [x] Unit suite — 1470 passed (new `test_make_sse_response_terminates_subscription_when_response_uninvoked` is red without the finalize, green with — confirmed) - [x] Code-reviewer independently re-verified discriminating power (removed `weakref.finalize`, observed test failure on the right assertion) plus `Depends(yield)` cancellation behavior - [ ] CI runs e2e/integration suite 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🐛 Fix support for
StreamingResponses with dependencies withyieldorUploadFiles, close after the response is doneThere's (again) an
AsyncExitStackmiddleware, now used mainly to close objects after the response is done.There's also a new wrapper duplicating some logic in Starlette but creating an
AsyncExitStackfor the exit code in dependencies withyield, for regular responses and WebSockets as well.The exit code of dependencies with
yieldis now executed after the response is done.