Merge pull request #22198 from mvdbeek/cached-route-lookup

[26.0] Cache url path lookup
This commit is contained in:
Marius van den Beek
2026-03-24 15:45:56 +01:00
committed by GitHub
3 changed files with 37 additions and 1 deletions
+16
View File
@@ -40,6 +40,7 @@ from galaxy.web.framework.base import walk_controller_modules
if TYPE_CHECKING:
from starlette.background import BackgroundTask
from starlette.routing import BaseRoute
from starlette.types import (
Receive,
Scope,
@@ -251,6 +252,21 @@ def add_request_id_middleware(app: FastAPI):
app.add_middleware(RawContextMiddleware, plugins=(RequestIdPlugin(force_new_uuid=True),))
def build_route_name_index(app: FastAPI) -> dict[str, list["BaseRoute"]]:
"""Build a name -> [route] index for O(1) route lookup.
Routes are immutable after app startup, so this index is built once
and reused for all subsequent requests. For most route names there
is exactly one candidate, making lookups O(1) instead of O(n).
"""
index: dict[str, list[BaseRoute]] = {}
for route in app.routes:
name = getattr(route, "name", None)
if name:
index.setdefault(name, []).append(route)
return index
def include_all_package_routers(app: FastAPI, package_name: str):
responses: dict[Union[int, str], dict[str, Any]] = {
"4XX": {
+19 -1
View File
@@ -223,7 +223,7 @@ class UrlBuilder:
else:
url = str(self.request.url_for(name, **path_params))
else:
url = self.request.app.url_path_for(name, **path_params)
url = self._url_path_for(name, **path_params)
if query_params:
url = f"{url}?{urlencode(query_params)}"
return url
@@ -233,6 +233,24 @@ class UrlBuilder:
path_params.update(query_params)
return web.url_for(name, **path_params)
def _url_path_for(self, name: str, **path_params) -> str:
"""O(1) route lookup using the app's pre-built name index.
The index is built once at startup in initialize_fast_app and maps
route names to their route objects, replacing Starlette's O(n)
linear scan with a direct dict lookup.
"""
candidates = self.request.app.state.route_name_index.get(name)
if candidates is not None:
for route in candidates:
try:
return route.url_path_for(name, **path_params)
except NoMatchFound:
pass
raise NoMatchFound(name, path_params)
# Fallback for names not in the index (e.g. Mount sub-routes)
return self.request.app.url_path_for(name, **path_params)
class GalaxyASGIRequest(GalaxyAbstractRequest):
"""Wrapper around Starlette/FastAPI Request object.
+2
View File
@@ -24,6 +24,7 @@ from galaxy.webapps.base.api import (
add_exception_handler,
add_raw_context_middlewares,
add_request_id_middleware,
build_route_name_index,
GalaxyFileResponse,
include_all_package_routers,
)
@@ -214,6 +215,7 @@ def initialize_fast_app(gx_wsgi_webapp, gx_app):
wsgi_handler = WSGIMiddleware(gx_wsgi_webapp)
gx_app.haltables.append(("WSGI Middleware threadpool", wsgi_handler.executor.shutdown))
include_tus(app, gx_app)
app.state.route_name_index = build_route_name_index(app)
app.mount("/", wsgi_handler) # type: ignore[arg-type]
if gx_app.config.galaxy_url_prefix != "/":
parent_app = FastAPI()