Skip to content

Keep get_dag_details and get_pools database queries off the API server event loop - #74247

Merged
henry3260 merged 2 commits into
apache:mainfrom
schizophrenicmaniac:fix/74239-api-server-event-loop-blocks-on
Oct 7, 2026
Merged

henry3260 merged 2 commits into
apache:mainfrom
schizophrenicmaniac:fix/74239-api-server-event-loop-blocks-on

Conversation

@schizophrenicmaniac

Copy link
Copy Markdown
Contributor

FastAPI validates a sync route's response in the threadpool but serializes it on the event loop. Two public API responses were still hitting the database during serialization, so when the SQLAlchemy pool was exhausted the whole API server stalled on the pool checkout until pool_timeout expired.

  • DAGDetailsResponse.latest_dag_version was a computed field that opened its own session to load the latest DagVersion. get_dag_details now loads it through the request session and sets it on the model, like is_favorite and active_runs_count, and latest_dag_version is a regular field. This also drops the second connection each /details request was taking.
  • PoolCollectionResponse.pools was Iterable[PoolResponse], which pydantic validates lazily during serialization, so the six slot queries per pool ran inside dump_json. It is now list[PoolResponse], so they run when the route builds the response in the threadpool.

In the OpenAPI schema latest_dag_version stays required and nullable, but it is no longer marked readOnly. I regenerated the spec, the UI client and the airflow-ctl datamodels.

The new tests record SQL statements executed on the event loop thread while serving /dags/{dag_id}/details and /pools. Without the change they catch 1 and 24 statements respectively.

closes: #74239


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@rjgoyln rjgoyln left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! The static-checks failure looks unrelated.

@henry3260
henry3260 force-pushed the fix/74239-api-server-event-loop-blocks-on branch from dac87e4 to 36d5760 Compare October 6, 2026 13:31

@henry3260 henry3260 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIce!

@henry3260
henry3260 merged commit 9af3db0 into apache:main Oct 7, 2026
308 of 309 checks passed
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Backport failed to create: airflow-ctl/v0-1-test. View the failure log Run details

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

Status Branch Result
❌ airflow-ctl/v0-1-test Commit Link

You can attempt to backport this manually by running:

cherry_picker 9af3db0 airflow-ctl/v0-1-test

This should apply the commit to the airflow-ctl/v0-1-test branch and leave the commit in conflict state marking
the files that need manual conflict resolution.

After you have resolved the conflicts, you can continue the backport process by running:

cherry_picker --continue

If you don't have cherry-picker installed, see the installation guide.

henry3260 added a commit to henry3260/airflow that referenced this pull request Nov 7, 2026
A response field annotated with Iterable is validated by pydantic lazily,
while FastAPI serializes the response on the event loop. Each item's
attributes are resolved there, on the sync engine: a lazy relationship or a
selectin post-load becomes a query that blocks the whole API server once
the connection pool is busy.

PoolCollectionResponse.pools was fixed by hand in apache#74247, which left an
explanatory comment in the datamodel, and the dags and deadlines fields in
apache#76525. Twenty fields still have the shape, and nothing stops a new one
from being added, because nothing about the annotation hints at the cost:
list[...] validates on construction, inside the route, and renders to the
same type: array in the OpenAPI schema, so the two read as interchangeable.

The known fields are grandfathered as strict xfails rather than fixed here,
so the list stays visible in the test report and can only shrink. The shape
of the check follows the one proposed for the same class of problem in
apache#76424.

Incomplete models are rebuilt before their fields are read. An annotation
naming something defined later in its own module stays an unparsed
ForwardRef that get_origin cannot see through, as AssetResponse.aliases
does, so a lazy field behind one would go unnoticed.
henry3260 added a commit to henry3260/airflow that referenced this pull request Nov 7, 2026
A response field annotated with Iterable is validated by pydantic lazily,
while FastAPI serializes the response on the event loop. Each item's
attributes are resolved there, on the sync engine: a lazy relationship or a
selectin post-load becomes a query that blocks the whole API server once
the connection pool is busy.

PoolCollectionResponse.pools was fixed by hand in apache#74247, which left an
explanatory comment in the datamodel, and the dags and deadlines fields in
apache#76525. Twenty fields still have the shape, and nothing stops a new one
from being added, because nothing about the annotation hints at the cost:
list[...] validates on construction, inside the route, and renders to the
same type: array in the OpenAPI schema, so the two read as interchangeable.

The known fields are grandfathered as strict xfails rather than fixed here,
so the list stays visible in the test report and can only shrink. The shape
of the check follows the one proposed for the same class of problem in
apache#76424.

Incomplete models are rebuilt before their fields are read. An annotation
naming something defined later in its own module stays an unparsed
ForwardRef that get_origin cannot see through, as AssetResponse.aliases
does, so a lazy field behind one would go unnoticed.
henry3260 added a commit to henry3260/airflow that referenced this pull request Nov 7, 2026
A response field annotated with Iterable is validated by pydantic lazily,
while FastAPI serializes the response on the event loop. Each item's
attributes are resolved there, on the sync engine: a lazy relationship or a
selectin post-load becomes a query that blocks the whole API server once
the connection pool is busy.

PoolCollectionResponse.pools was fixed by hand in apache#74247, which left an
explanatory comment in the datamodel, and the dags and deadlines fields in
apache#76525. Twenty fields still have the shape, and nothing stops a new one
from being added, because nothing about the annotation hints at the cost:
list[...] validates on construction, inside the route, and renders to the
same type: array in the OpenAPI schema, so the two read as interchangeable.

The known fields are grandfathered as strict xfails rather than fixed here,
so the list stays visible in the test report and can only shrink. The shape
of the check follows the one proposed for the same class of problem in
apache#76424.

Incomplete models are rebuilt before their fields are read. An annotation
naming something defined later in its own module stays an unparsed
ForwardRef that get_origin cannot see through, as AssetResponse.aliases
does, so a lazy field behind one would go unnoticed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:airflow-ctl area:API Airflow's REST/HTTP API area:UI Related to UI/UX. For Frontend Developers. backport-to-airflow-ctl/v0-1-test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API server event loop blocks on database queries while serializing get_dag_details and get_pools responses

3 participants