The @app.middleware("http") decorator uses Starlette's BaseHTTPMiddleware
which buffers the entire response body through an anyio memory channel
between two tasks.
Replace with a pure ASGI middleware that injects the X-Frame-Options
header directly into the response start message, avoiding the buffering
entirely.
Shows a user-facing error instead of silently do nothing when instance removal fails.
Adds focused coverage for successful and failed removal flows across both supported instance types.
Allows metadata-only updates to skip variable validation when no new variable values are provided.
Prevents hide (remove) operations from failing for instances backed by templates with required variables, while preserving the previously stored values.
Adds regression coverage for both supported instance types to ensure hidden updates remain safe and predictable.
Non-string values (e.g. dicts) in replacement_params cause a
ProgrammingError when inserting into the TEXT column of
workflow_request_input_parameters.
Fixes#22247
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The batch purge_history_datasets celery task uses raw SQL for bulk
operations, bypassing SQLAlchemy's onupdate=now trigger on User.update_time.
This caused archiving with purge to leave the user's update_time stale.
Also forwards preserve_owner_update_time through to the batch task so
admin-initiated purges of other users' histories still preserve the
owner's update_time.
Replace the per-request route cache with a name -> [route] index
built once at startup in initialize_fast_app and stored on app.state.
This avoids Starlette's O(n) linear route scan on every url_path_for
call, giving O(1) lookups (~400x speedup).
This addresses the concern about multiple routes sharing the
same name: the index maps each name to the full list of candidate
routes. At lookup time we try each candidate until one matches the
provided path_params, preserving the same semantics as Starlette's
linear scan. For example, prepare_collection_download correctly
resolves to different URLs depending on whether history_id is in the
params or not. The index is also fixed-size (bounded by the number of
route names, not by request volume), so there is no unbounded cache
growth.
Move get_legitimate_roles, get_private_role_user_emails_dict, and
can_manage_dataset calls into the conditional branch where they are
actually needed (only when user is logged in AND dataset has permission
actions set). This avoids expensive full-table role/email queries for
anonymous users, public datasets, and non-shareable datasets.
Also scope get_private_role_user_emails_dict to only fetch email
mappings for the specific role IDs returned by get_legitimate_roles,
rather than scanning all private roles in the system.
Switch from legacy controller axios calls to the typed GalaxyApi() fetcher
for both loading and reloading tool data tables. Remove the now-unused
tool_data_table_info, tool_data_table_info_1, and reload_tool_data_tables
methods from the data_manager controller.
Fixes https://github.com/galaxyproject/galaxy/issues/22227
When a search reset and a new search triggered concurrent load() calls,
a stale response from the reset could overwrite the correct search
results. Use a generation counter to discard responses from superseded
loads.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed in starlette 1.0, see https://github.com/Kludex/starlette/pull/3147
Fix the following mypy errors in package tests:
```
galaxy/webapps/base/api.py:94: error: Too many arguments for "__init__" of
"FileResponse" [call-arg]
super().__init__(
^~~~~~~~~~~~~~~~~
galaxy/webapps/base/api.py:95: error: Argument 8 to "__init__" of
"FileResponse" has incompatible type "str | None"; expected "str" [arg-type]
..., media_type, background, filename, stat_result, method, content_dispo...
^~~~~~
```
and update its dependencies. This is needed to drop the requirement
on redis 7.1.0, which conflicts with our conditional requirement
`redis>=5.3.0,<6` , which is needed for compatibility with celery 5.6.2 .
Fix https://github.com/galaxyproject/galaxy/issues/22201 .
Multiple routes can share a name but expect different parameter sets.
Using just the name as cache key could return the wrong route when
path_params differ. Key on (name, frozenset(path_params)) instead.
Async def route handlers were calling synchronous DB queries, DNS
lookups, and HTTP APIs directly on the event loop, blocking all
concurrent requests on the same worker.
- chat.py: use await agent.run() instead of agent.run_sync(), use
AsyncOpenAI instead of sync openai client, wrap all chat_manager
and job_manager DB calls with anyio.to_thread.run_sync
- agents.py: wrap chat_manager/job_manager DB calls in analyze_error
and create_custom_tool with anyio.to_thread.run_sync
- proxy.py: wrap _validate_url_and_access (blocking socket.getaddrinfo)
with anyio.to_thread.run_sync
- error_analysis.py: wrap job_manager.get_accessible_job with
anyio.to_thread.run_sync