mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-30 18:13:23 +08:00
Merge pull request #22198 from mvdbeek/cached-route-lookup
[26.0] Cache url path lookup
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user