Skip to content

Commit 336bb49

Browse files
fix(server): FastAPI 0.137 compatibility — skip route deletion when router is referenced (#22277)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Alexander Streed <alex.s@prefect.io>
1 parent 47fd833 commit 336bb49

1 file changed

Lines changed: 17 additions & 14 deletions

File tree

src/prefect/server/api/server.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import contextlib
1111
import gc
1212
import hmac
13+
import importlib.metadata
1314
import logging
1415
import mimetypes
1516
import os
@@ -41,6 +42,7 @@
4142
from fastapi.openapi.utils import get_openapi
4243
from fastapi.responses import JSONResponse, RedirectResponse
4344
from fastapi.staticfiles import StaticFiles
45+
from packaging.version import Version
4446
from starlette.exceptions import HTTPException
4547
from typing_extensions import Self
4648

@@ -73,6 +75,12 @@
7375

7476
logfire: Any | None = configure_logfire()
7577

78+
# FastAPI < 0.137 copies routes when including a router; 0.137+ keeps a
79+
# reference to the original via _IncludedRouter.
80+
_FASTAPI_COPIES_ROUTES_ON_INCLUDE: bool = Version(
81+
importlib.metadata.version("fastapi")
82+
) < Version("0.137.0")
83+
7684
TITLE = "Prefect Server"
7785
API_TITLE = "Prefect REST API"
7886
UI_TITLE = "Prefect REST API UI"
@@ -491,21 +499,16 @@ async def server_version() -> str: # type: ignore[reportUnusedFunction]
491499

492500
for router in API_ROUTERS:
493501
api_app.include_router(router, dependencies=dependencies)
494-
if final:
495-
# Important note about how FastAPI works:
496-
#
497-
# When including a router, FastAPI copies the routes and builds entirely new
498-
# Pydantic models to represent the request bodies of the routes in the
499-
# router. This is because the dependencies may change if the same router is
500-
# included multiple times, but it also means that we are holding onto an
501-
# entire set of Pydantic models on the original routers for the duration of
502-
# the server process that will never be used.
502+
if final and _FASTAPI_COPIES_ROUTES_ON_INCLUDE:
503+
# When including a router, older versions of FastAPI (< 0.137) copy
504+
# the routes and build entirely new Pydantic models. Since Prefect
505+
# does not reuse routers, we can delete the originals to reclaim
506+
# ~50-55 MB of memory.
503507
#
504-
# Because Prefect does not reuse routers, we are free to clean up the routes
505-
# because we know they won't be used again. Thus, if we have the hint that
506-
# this is the final instance we will create in this process, we can clean up
507-
# the routes on the original source routers to conserve memory (~50-55MB as
508-
# of introducing this change).
508+
# FastAPI 0.137+ wraps included routers in an _IncludedRouter that
509+
# references the original router for request matching, so the
510+
# originals must be kept. The duplication no longer occurs in this
511+
# case either, so the optimisation is unnecessary.
509512
del router.routes
510513

511514
if final:

0 commit comments

Comments
 (0)