Skip to content

Add draining state for graceful Dag pausing - #72407

Merged
dheerajturaga merged 12 commits into
apache:mainfrom
dheerajturaga:add-dag-draining-state
Sep 14, 2026
Merged

dheerajturaga merged 12 commits into
apache:mainfrom
dheerajturaga:add-dag-draining-state

Conversation

@dheerajturaga

@dheerajturaga dheerajturaga commented Sep 2, 2026 •

Copy link
Copy Markdown
Member

Pausing a Dag today is abrupt: it stops the Dag mid-flight, letting running tasks
finish but stranding queued and downstream tasks until it is unpaused. There is no
way to say "stop starting new scheduled runs, but let the work in flight finish."

That gap bites hardest during upgrades and maintenance windows. The workaround in
the wild is to rewrite every Dag's schedule to None, wait for runs to drain, do
the work, then put the schedules back — invasive, and easy to get wrong.

This adds a draining scheduling state that expresses it directly:

  • Draining blocks new scheduler-created Dag runs, including scheduled and
    asset-triggered runs, while letting task instances in queued and running runs
    continue to be scheduled.
  • Explicit triggers remain available while draining, matching paused-Dag behavior.
    A manual, operator-triggered, asset-materialization, or backfill request extends
    the drain until the resulting work finishes.
  • The scheduler moves a draining Dag to paused once no unfinished runs remain and
    no newly accepted backfill is still initializing, recording a drain_completed
    audit log entry.

Draining is transient, not a third resting state. It always converges to
paused; the resting states are still active and paused. The UI reflects that —
the pause control stays a binary toggle, and drain is offered at the one moment it
carries meaning: flipping a Dag off while it still has unfinished runs asks whether
to drain then pause or pause now. A Dag with nothing running pauses in a single
click as before, since the two are equivalent there.

API. DAGResponse gains a scheduling_state field. PATCH /dags/{dag_id}
accepts either is_paused or scheduling_state and rejects a body carrying both.
Existing is_paused clients are unaffected.

Migration. Adds dag.is_draining (non-null, default false) with an index and a
NOT (is_paused AND is_draining) check constraint. It also adds a supporting index
for identifying an initializing backfill by Dag.

closes: #22006

Screenshots:

Drain Dialogue image image image
Was generative AI tooling used to co-author this PR?
  • Yes — Codex (GPT-5) and Claude Code (Opus 5)

Generated-by: Codex (GPT-5), Claude Code (Opus 5) following the guidelines

Backfill metadata becomes visible before its Dag runs are committed. Without protecting that initialization window, the scheduler can pause a draining Dag before the accepted backfill has work available to keep the drain open.
Ensure the Active Dags dashboard card opens the same exact set represented by its count while preserving the established meaning of the paused filter.
Stats.tsx now links the active-Dags card to dags?scheduling_state=active
instead of dags?paused=false, but the e2e spec still asserted the old
query param, breaking Chromium/Firefox/WebKit e2e CI.
@dheerajturaga dheerajturaga added the ready for maintainer review Set after triaging when all criteria pass. label Sep 13, 2026
@eladkal

eladkal commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Tested locally. Works nicely!
I have some comments but we can talk about them post merge

@dheerajturaga
dheerajturaga merged commit 35f33ed into apache:main Sep 14, 2026
302 of 303 checks passed
@github-actions

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 35f33ed 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.

dheerajturaga added a commit to dheerajturaga/airflow that referenced this pull request Sep 14, 2026
Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in apache#72407.

related: apache#22006

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D57QaXubhpy8CDH4c23KcP
dheerajturaga added a commit to dheerajturaga/airflow that referenced this pull request Sep 14, 2026
Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in apache#72407.

related: apache#22006
@dheerajturaga dheerajturaga mentioned this pull request Sep 15, 2026
1 task done
dheerajturaga added a commit to dheerajturaga/airflow that referenced this pull request Sep 16, 2026
Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in apache#72407.

related: apache#22006
dheerajturaga added a commit to dheerajturaga/airflow that referenced this pull request Sep 22, 2026
Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in apache#72407.

related: apache#22006
dheerajturaga added a commit to dheerajturaga/airflow that referenced this pull request Sep 24, 2026
Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in apache#72407.

related: apache#22006
dheerajturaga added a commit to dheerajturaga/airflow that referenced this pull request Sep 24, 2026
Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in apache#72407.

related: apache#22006
dheerajturaga added a commit to dheerajturaga/airflow that referenced this pull request Sep 25, 2026
Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in apache#72407.

related: apache#22006
dheerajturaga added a commit to dheerajturaga/airflow that referenced this pull request Sep 25, 2026
Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in apache#72407.

related: apache#22006
bbovenzi pushed a commit that referenced this pull request Sep 26, 2026
* Add a bulk pause/drain action to the Dags list

Draining a Dag with unfinished runs now asks whether to drain or pause
immediately, which turned stopping many Dags into two clicks per Dag
instead of one. Selecting several Dags in the table view and pausing
or draining them together needs at most one such choice for the whole
batch.

Follow-up to the draining state added in #72407.

related: #22006

* Mention bulk pause/drain in the Dag draining release note

The bulk action in the Dags list is part of the graceful draining feature, so the release note should credit it alongside the core and airflowctl changes.

* Keep already-paused Dags paused when bulk draining

Draining clears is_paused, so a paused Dag caught up in a bulk drain let
the scheduler resume its unfinished runs until the drain completed, the
opposite of what the user asked for. Such Dags also forced the
drain-vs-pause prompt even when every unpaused Dag in the selection was
idle.

* Add a bulk unpause action to the Dags list

A selection could be paused or drained in one go but had to be resumed
one toggle at a time. Unpausing always asks for confirmation because
resuming many Dags at once can start a burst of catchup runs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ability to disable a running DAG only after after it's in a finished state

3 participants