Skip to content

Eager-load consumed asset events relations in ti_run - #76565

Open
arifrhm wants to merge 3 commits into
apache:mainfrom
arifrhm:fix-ti-run-asset-events-eager-load
Open

arifrhm wants to merge 3 commits into
apache:mainfrom
arifrhm:fix-ti-run-asset-events-eager-load

Conversation

@arifrhm

@arifrhm arifrhm commented Nov 5, 2026 •

Copy link
Copy Markdown

closes: #76558

Description

In ti_run (PATCH /execution/task-instances/{id}/run), when a DAG Run is triggered by many asset events, serializing consumed_asset_events on DagRun triggers lazy N+1 queries for AssetEvent.asset and AssetEvent.source_aliases for each event inside a transaction holding a FOR UPDATE OF task_instance row lock. When runs have thousands of asset events, this leads to task execution API statement timeouts.

This PR implements Option 1 outlined in #76558 by using selectinload to eager-load AssetEvent.asset and AssetEvent.source_aliases along with DR.consumed_asset_events, mirroring the pattern already used in _eager_load_dag_run_for_validation() in scheduler_job_runner.py. This serves as a lightweight, non-breaking fix/PoC that resolves the database row lock bottleneck without requiring changes to the Task SDK wire protocol.

Testing

  • Added unit test test_ti_run_consumed_asset_events_eager_loaded in airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_task_instances.py verifying that both AssetEvent.asset and AssetEvent.source_aliases are preloaded in the run context response across multiple events.
Empirical Query Profiling on PostgreSQL 16 (N+1 vs selectinload)

Benchmarked against PostgreSQL 16 with 1 DagRun consuming 5 AssetEvents (each linked to an Asset and an AssetAlias):

  • Before (joinedload only on events): 11 queries (1 DR + 5 individual queries for assets + 5 individual queries for aliases → O(N) under FOR UPDATE lock)
  • After (selectinload nested relations): 4 queries (1 DR + 1 batched events + 1 batched assets + 1 batched aliases → O(1) constant)
  • Observed SQL on PostgreSQL:
    1. SELECT dag_run ... WHERE dag_id = ... AND run_id = ...
    2. SELECT ... FROM dagrun_asset_event JOIN asset_event ... WHERE dag_run_id IN (...)
    3. SELECT ... FROM asset_alias_event JOIN asset_alias ... WHERE event_id IN ($1, $2, $3, $4, $5)
    4. SELECT ... FROM asset WHERE asset.id IN ($1, $2, $3, $4, $5)
  • Data equality: Verified 100% identical deserialized output.

Was generative AI tooling used to co-author this PR?
  • Yes (Antigravity)

Generated-by: Antigravity following the guidelines

In ti_run (PATCH /execution/task-instances/{id}/run), when a DAG Run is triggered by many asset events, serializing consumed_asset_events triggers lazy N+1 queries for AssetEvent.asset and AssetEvent.source_aliases for each event inside a transaction holding a FOR UPDATE OF task_instance row lock. This causes task execution API statement timeouts.

This change uses selectinload to eager-load AssetEvent.asset and AssetEvent.source_aliases along with DR.consumed_asset_events, mirroring the pattern used in _eager_load_dag_run_for_validation() in scheduler_job_runner.py.
@boring-cyborg boring-cyborg Bot added area:API Airflow's REST/HTTP API area:task-sdk labels Nov 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API area:task-sdk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Runs with many consumed asset events lead to Task execution API timeouts

1 participant