Repository navigation
Fix memory issue: remove eager loading of all TIs in scheduler - #60956
Merged
Merged
Conversation
The scheduler's `get_running_dag_runs_to_examine()` was using `joinedload(DagRun.task_instances)` which loaded ALL TaskInstances for each DagRun into memory. This could cause significant memory pressure with large DAGs having many tasks. Changes: - Remove unnecessary `joinedload(task_instances)` from the query - Convert the TI loop in `_verify_integrity_if_dag_changed` to a bulk UPDATE statement, avoiding loading TIs into memory entirely - Add `session.expire()` to ensure relationship cache coherence This reduces memory usage in the scheduler's hot path, especially beneficial for DAGs with hundreds of tasks.
Member
Author
|
cc @vatsrahul1001 Flagging for testing and review |
jedcunningham
approved these changes
Jan 22, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses a significant memory inefficiency in the Airflow scheduler by removing eager loading of all TaskInstances during DAG run examination, which occurs every scheduler loop (~1/second). The change eliminates unnecessary memory pressure when processing large DAGs.
Changes:
- Removed
joinedload(task_instances)fromget_running_dag_runs_to_examine()query - Replaced TI iteration loop with bulk UPDATE statement in
_verify_integrity_if_dag_changed() - Added session cache expiration to maintain relationship coherence after bulk updates
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| airflow-core/src/airflow/models/dagrun.py | Removes eager loading of task_instances relationship to prevent loading all TIs into memory |
| airflow-core/src/airflow/jobs/scheduler_job_runner.py | Converts TI loop to bulk UPDATE query and adds session expiration for cache coherence |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
kaxil
marked this pull request as ready for review
January 22, 2026 23:29
suii2210
pushed a commit
to suii2210/airflow
that referenced
this pull request
Jan 26, 2026
…e#60956) The scheduler's `get_running_dag_runs_to_examine()` was using `joinedload(DagRun.task_instances)` which loaded ALL TaskInstances for each DagRun into memory. This could cause significant memory pressure with large DAGs having many tasks. Changes: - Remove unnecessary `joinedload(task_instances)` from the query - Convert the TI loop in `_verify_integrity_if_dag_changed` to a bulk UPDATE statement, avoiding loading TIs into memory entirely - Add `session.expire()` to ensure relationship cache coherence This reduces memory usage in the scheduler's hot path, especially beneficial for DAGs with hundreds of tasks.
shreyas-dev
pushed a commit
to shreyas-dev/airflow
that referenced
this pull request
Jan 29, 2026
…e#60956) The scheduler's `get_running_dag_runs_to_examine()` was using `joinedload(DagRun.task_instances)` which loaded ALL TaskInstances for each DagRun into memory. This could cause significant memory pressure with large DAGs having many tasks. Changes: - Remove unnecessary `joinedload(task_instances)` from the query - Convert the TI loop in `_verify_integrity_if_dag_changed` to a bulk UPDATE statement, avoiding loading TIs into memory entirely - Add `session.expire()` to ensure relationship cache coherence This reduces memory usage in the scheduler's hot path, especially beneficial for DAGs with hundreds of tasks.
jhgoebbert
pushed a commit
to jhgoebbert/airflow_Owen-CH-Leung
that referenced
this pull request
Feb 8, 2026
…e#60956) The scheduler's `get_running_dag_runs_to_examine()` was using `joinedload(DagRun.task_instances)` which loaded ALL TaskInstances for each DagRun into memory. This could cause significant memory pressure with large DAGs having many tasks. Changes: - Remove unnecessary `joinedload(task_instances)` from the query - Convert the TI loop in `_verify_integrity_if_dag_changed` to a bulk UPDATE statement, avoiding loading TIs into memory entirely - Add `session.expire()` to ensure relationship cache coherence This reduces memory usage in the scheduler's hot path, especially beneficial for DAGs with hundreds of tasks.
Member
|
Nice! |
choo121600
pushed a commit
to choo121600/airflow
that referenced
this pull request
Feb 22, 2026
…e#60956) The scheduler's `get_running_dag_runs_to_examine()` was using `joinedload(DagRun.task_instances)` which loaded ALL TaskInstances for each DagRun into memory. This could cause significant memory pressure with large DAGs having many tasks. Changes: - Remove unnecessary `joinedload(task_instances)` from the query - Convert the TI loop in `_verify_integrity_if_dag_changed` to a bulk UPDATE statement, avoiding loading TIs into memory entirely - Add `session.expire()` to ensure relationship cache coherence This reduces memory usage in the scheduler's hot path, especially beneficial for DAGs with hundreds of tasks.
Subham-KRLX
pushed a commit
to Subham-KRLX/airflow
that referenced
this pull request
Mar 4, 2026
…e#60956) The scheduler's `get_running_dag_runs_to_examine()` was using `joinedload(DagRun.task_instances)` which loaded ALL TaskInstances for each DagRun into memory. This could cause significant memory pressure with large DAGs having many tasks. Changes: - Remove unnecessary `joinedload(task_instances)` from the query - Convert the TI loop in `_verify_integrity_if_dag_changed` to a bulk UPDATE statement, avoiding loading TIs into memory entirely - Add `session.expire()` to ensure relationship cache coherence This reduces memory usage in the scheduler's hot path, especially beneficial for DAGs with hundreds of tasks.
Ankurdeewan
pushed a commit
to Ankurdeewan/airflow
that referenced
this pull request
Mar 15, 2026
…e#60956) The scheduler's `get_running_dag_runs_to_examine()` was using `joinedload(DagRun.task_instances)` which loaded ALL TaskInstances for each DagRun into memory. This could cause significant memory pressure with large DAGs having many tasks. Changes: - Remove unnecessary `joinedload(task_instances)` from the query - Convert the TI loop in `_verify_integrity_if_dag_changed` to a bulk UPDATE statement, avoiding loading TIs into memory entirely - Add `session.expire()` to ensure relationship cache coherence This reduces memory usage in the scheduler's hot path, especially beneficial for DAGs with hundreds of tasks.
1 task done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The scheduler's
get_running_dag_runs_to_examine()was usingjoinedload(DagRun.task_instances)which loaded ALL TaskInstances for each DagRun into memory. This happens every scheduler loop and could cause significant memory pressure with large DAGs.Changes:
joinedload(task_instances)from the query_verify_integrity_if_dag_changedto a bulk UPDATE statementsession.expire()to ensure relationship cache coherenceBenchmark Results
Benchmarked the actual
DagRun.get_running_dag_runs_to_examine()method:Setup: 20 running DAG runs × 100 tasks = 2,000 TIs (matches
DEFAULT_DAGRUNS_TO_EXAMINE)This method is called every scheduler loop (~1/second), so this eliminates ~6.4 MB of memory churn per second.
Run the Benchmark
Benchmark Script
Technical Details
Why both UPDATE and verify_integrity() are needed
dag_version_idon existing unfinished TIsSession synchronization
The
session.expire(dag_run, ["task_instances"])follows SQLAlchemy best practices for bulk operations that bypass the ORM.Was generative AI tooling used to co-author this PR?