Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions src/prefect/server/api/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import contextlib
import gc
import hmac
import importlib.metadata
import logging
import mimetypes
import os
Expand Down Expand Up @@ -41,6 +42,7 @@
from fastapi.openapi.utils import get_openapi
from fastapi.responses import JSONResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from packaging.version import Version
from starlette.exceptions import HTTPException
from typing_extensions import Self

Expand Down Expand Up @@ -73,6 +75,12 @@

logfire: Any | None = configure_logfire()

# FastAPI < 0.137 copies routes when including a router; 0.137+ keeps a
# reference to the original via _IncludedRouter.
_FASTAPI_COPIES_ROUTES_ON_INCLUDE: bool = Version(
importlib.metadata.version("fastapi")
) < Version("0.137.0")

TITLE = "Prefect Server"
API_TITLE = "Prefect REST API"
UI_TITLE = "Prefect REST API UI"
Expand Down Expand Up @@ -491,21 +499,16 @@ async def server_version() -> str: # type: ignore[reportUnusedFunction]

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

if final:
Expand Down
Loading