Skip to content

Speed up Dags list and dashboard queries on large DagRun tables - #67721

Merged
pierrejeambrun merged 3 commits into
apache:mainfrom
yuseok89:improve-dagrun-query-scalability
Jun 10, 2026
Merged

pierrejeambrun merged 3 commits into
apache:mainfrom
yuseok89:improve-dagrun-query-scalability

Conversation

@yuseok89

@yuseok89 yuseok89 commented May 29, 2026 •

Copy link
Copy Markdown
Contributor

While reviewing #67242, we found that several existing UI queries are also slower than expected on large dag_run tables (verified with ~5M DagRuns). Adding new indexes would be a larger change (a migration), so this PR focuses on reusing the existing indexes. If there's a better direction, I'd appreciate your input.

Changes (per query)

  • Dashboard historical_metrics: window now filters on the indexed run_after instead of the unindexed start_date/end_date.
  • Dashboard dag_stats: latest run per Dag via correlated ORDER BY logical_date DESC LIMIT 1 instead of MAX(...) GROUP BY scanning every run.
  • Dags list get_dags recent runs: top-N per Dag via ORDER BY run_after DESC LIMIT N instead of rank() over all runs.

Screenshots

Before

Screenshot 2026-05-29 at 11 30 26 PM Screenshot 2026-05-29 at 11 33 52 PM

After

image image

cc @pierrejeambrun


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

  • 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.

@boring-cyborg boring-cyborg Bot added the area:API Airflow's REST/HTTP API label May 29, 2026

@choo121600 choo121600 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall looks good to me :)

@potiuk potiuk added the ready for maintainer review Set by triage when all criteria pass, or by the fork CI reconciler when fork CI is green. label Jun 1, 2026

@pierrejeambrun pierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thanks

@pierrejeambrun
pierrejeambrun merged commit a48c549 into apache:main Jun 10, 2026
281 checks passed
@vatsrahul1001 vatsrahul1001 added this to the Airflow 3.3.0 milestone Jun 15, 2026
pickfire added a commit to pickfire/airflow that referenced this pull request Oct 7, 2026
…ables

The per-Dag UNION ALL branches introduced in apache#67721 plan as backward index
scans of the global idx_dag_run_run_after with a Filter on dag_id. On a
large, skewed dag_run table this is catastrophic for "cold" Dags — those
whose last run is months old or that never ran: the scan must walk
essentially every row of every *other* Dag before reaching that branch's
rows (measured on a 3.8M-row metadata DB: 3,800,261 rows removed by filter,
~2.3s for a single cold branch, 30-60s per page — gateway 504s), because
at default statistics the planner misestimates cold dag_ids (they fall
outside the MCV list) and bets the backward scan hits LIMIT matches
quickly. Hot pages are affected too, bistably: the identical statement
was observed flipping 22.8ms <-> 141s across a single ANALYZE.

Replace the 50-branch union with one page-scoped window query:

  row_number() OVER (PARTITION BY dag_id ORDER BY run_after DESC, id DESC)

over WHERE dag_id IN (page's dag ids), filtered to rn <= dag_runs_limit.
This restores the pre-apache#67721 access path (per-Dag probes of
idx_dag_run_dag_id + one sort + WindowAgg), bounded regardless of where
each Dag's runs sit in time or what the planner estimates, while keeping
apache#67721's exact-N-per-Dag semantics (row_number ≡ per-branch LIMIT,
including ties — the pre-apache#67721 rank() returned extra rows on run_after
ties) and fetching all columns inline (no join-back).

Measured end-to-end on a dockerized copy of a 3.8M-row production
metadata DB (PostgreSQL 15.14, default statistics, 74%-cold first page):

  GET /ui/dags?limit=50&dag_runs_limit=14:  31.2s -> 0.13-0.64s
  response byte-identical to the union (same rows, same order, 291/291
  id-set match, total_entries unchanged)

The window-function shape is portable (same ANSI syntax as the
pre-apache#67721 rank() query and the existing row_number usage in
common/db/assets.py), so the sqlite/mysql/mssql matrix is unaffected.

Co-Authored-By: Claude Code <noreply@anthropic.com>
pickfire added a commit to pickfire/airflow that referenced this pull request Oct 7, 2026
…ables

The per-Dag UNION ALL branches introduced in apache#67721 plan as backward index
scans of the global idx_dag_run_run_after with a Filter on dag_id. On a
large, skewed dag_run table this is catastrophic for "cold" Dags — those
whose last run is months old or that never ran: the scan must walk
essentially every row of every *other* Dag before reaching that branch's
rows (measured on a 3.8M-row metadata DB: 3,800,261 rows removed by filter,
~2.3s for a single cold branch, 30-60s per page — gateway 504s), because
at default statistics the planner misestimates cold dag_ids (they fall
outside the MCV list) and bets the backward scan hits LIMIT matches
quickly. Hot pages are affected too, bistably: the identical statement
was observed flipping 22.8ms <-> 141s across a single ANALYZE.

Replace the 50-branch union with one page-scoped window query:

  row_number() OVER (PARTITION BY dag_id ORDER BY run_after DESC, id DESC)

over WHERE dag_id IN (page's dag ids), filtered to rn <= dag_runs_limit.
This restores the pre-apache#67721 access path (per-Dag probes of
idx_dag_run_dag_id + one sort + WindowAgg), bounded regardless of where
each Dag's runs sit in time or what the planner estimates, while keeping
apache#67721's exact-N-per-Dag semantics (row_number ≡ per-branch LIMIT,
including ties — the pre-apache#67721 rank() returned extra rows on run_after
ties) and fetching all columns inline (no join-back).

Measured end-to-end on a dockerized copy of a 3.8M-row production
metadata DB (PostgreSQL 15.14, default statistics, 74%-cold first page):

  GET /ui/dags?limit=50&dag_runs_limit=14:  31.2s -> 0.13-0.64s
  response byte-identical to the union (same rows, same order, 291/291
  id-set match, total_entries unchanged)

The window-function shape is portable (same ANSI syntax as the
pre-apache#67721 rank() query and the existing row_number usage in
common/db/assets.py), so the sqlite/mysql/mssql matrix is unaffected.

Co-Authored-By: Claude Code <noreply@anthropic.com>
pickfire added a commit to pickfire/airflow that referenced this pull request Oct 7, 2026
…ables

The per-Dag UNION ALL branches introduced in apache#67721 plan as backward index
scans of the global idx_dag_run_run_after with a Filter on dag_id. On a
large, skewed dag_run table this is catastrophic for "cold" Dags — those
whose last run is months old or that never ran: the scan must walk
essentially every row of every *other* Dag before reaching that branch's
rows (measured on a 3.8M-row metadata DB: 3,800,261 rows removed by filter,
~2.3s for a single cold branch, 30-60s per page — gateway 504s), because
at default statistics the planner misestimates cold dag_ids (they fall
outside the MCV list) and bets the backward scan hits LIMIT matches
quickly. Hot pages are affected too, bistably: the identical statement
was observed flipping 22.8ms <-> 141s across a single ANALYZE.

Replace the 50-branch union with one page-scoped window query:

  row_number() OVER (PARTITION BY dag_id ORDER BY run_after DESC, id DESC)

over WHERE dag_id IN (page's dag ids), filtered to rn <= dag_runs_limit.
This restores the pre-apache#67721 access path (per-Dag probes of
idx_dag_run_dag_id + one sort + WindowAgg), bounded regardless of where
each Dag's runs sit in time or what the planner estimates, while keeping
apache#67721's exact-N-per-Dag semantics. On run_after ties the window's
(id DESC) tiebreak deterministically keeps the latest-inserted row,
where the old per-branch LIMIT picked arbitrarily; the outer ORDER BY
leaves tied rows in unspecified order, same as the union. All columns
are selected inline (no join-back). dag_runs_limit is now NonNegativeInt,
matching limit/offset.

Measured end-to-end on a dockerized copy of a 3.8M-row production
metadata DB (PostgreSQL 15.14, default statistics, 74%-cold first page):

  GET /ui/dags?limit=50&dag_runs_limit=14:  31.2s -> 0.13-0.64s
  same rows and order returned (291/291 id-set match, total_entries
  unchanged); selection can differ from the union only on run_after ties,
  where the new pick is the newer run

Trade-off: the window sorts all runs of the page's Dags, so a page of the
50 highest-run-count Dags (~619K rows) costs ~2.0s with a 58MB external
sort (vs the union's 0.25-12s bistable plan) — linear and deterministic
instead of the 30-60s+ cold-page catastrophe.

The window-function shape is portable (same ANSI syntax as the
pre-apache#67721 rank() query and the existing row_number usage in
common/db/assets.py), so the sqlite/mysql/mssql matrix is unaffected.

Co-Authored-By: Claude Code <noreply@anthropic.com>
pickfire added a commit to pickfire/airflow that referenced this pull request Oct 8, 2026
…ables

The per-Dag UNION ALL branches introduced in apache#67721 plan as backward index
scans of the global idx_dag_run_run_after with a Filter on dag_id. On a
large, skewed dag_run table this is catastrophic for "cold" Dags — those
whose last run is months old or that never ran: the scan must walk
essentially every row of every *other* Dag before reaching that branch's
rows (measured on a 3.8M-row metadata DB: 3,800,261 rows removed by filter,
~2.3s for a single cold branch, 30-60s per page — gateway 504s), because
at default statistics the planner misestimates cold dag_ids (they fall
outside the MCV list) and bets the backward scan hits LIMIT matches
quickly. Hot pages are affected too, bistably: the identical statement
was observed flipping 22.8ms <-> 141s across a single ANALYZE.

Replace the 50-branch union with one page-scoped window query:

  row_number() OVER (PARTITION BY dag_id ORDER BY run_after DESC, id DESC)

over WHERE dag_id IN (page's dag ids), filtered to rn <= dag_runs_limit.
This restores the pre-apache#67721 access path (per-Dag probes of
idx_dag_run_dag_id + one sort + WindowAgg), bounded regardless of where
each Dag's runs sit in time or what the planner estimates, while keeping
apache#67721's exact-N-per-Dag semantics. On run_after ties the window's
(id DESC) tiebreak deterministically keeps the latest-inserted row,
where the old per-branch LIMIT picked arbitrarily; the outer ORDER BY
leaves tied rows in unspecified order, same as the union. All columns
are selected inline (no join-back). dag_runs_limit is now NonNegativeInt,
matching limit/offset.

Measured end-to-end on a dockerized copy of a 3.8M-row production
metadata DB (PostgreSQL 15.14, default statistics, 74%-cold first page):

  GET /ui/dags?limit=50&dag_runs_limit=14:  31.2s -> 0.13-0.64s
  same rows and order returned (291/291 id-set match, total_entries
  unchanged); selection can differ from the union only on run_after ties,
  where the new pick is the newer run

Trade-off: the window sorts all runs of the page's Dags, so a page of the
50 highest-run-count Dags (~619K rows) costs ~2.0s with a 58MB external
sort (vs the union's 0.25-12s bistable plan) — linear and deterministic
instead of the 30-60s+ cold-page catastrophe.

The window-function shape is portable (same ANSI syntax as the
pre-apache#67721 rank() query and the existing row_number usage in
common/db/assets.py), so the sqlite/mysql/mssql matrix is unaffected.

Co-Authored-By: Claude Code <noreply@anthropic.com>
pierrejeambrun pushed a commit to pickfire/airflow that referenced this pull request Oct 13, 2026
…ables

The per-Dag UNION ALL branches introduced in apache#67721 plan as backward index
scans of the global idx_dag_run_run_after with a Filter on dag_id. On a
large, skewed dag_run table this is catastrophic for "cold" Dags — those
whose last run is months old or that never ran: the scan must walk
essentially every row of every *other* Dag before reaching that branch's
rows (measured on a 3.8M-row metadata DB: 3,800,261 rows removed by filter,
~2.3s for a single cold branch, 30-60s per page — gateway 504s), because
at default statistics the planner misestimates cold dag_ids (they fall
outside the MCV list) and bets the backward scan hits LIMIT matches
quickly. Hot pages are affected too, bistably: the identical statement
was observed flipping 22.8ms <-> 141s across a single ANALYZE.

Replace the 50-branch union with one page-scoped window query:

  row_number() OVER (PARTITION BY dag_id ORDER BY run_after DESC, id DESC)

over WHERE dag_id IN (page's dag ids), filtered to rn <= dag_runs_limit.
This restores the pre-apache#67721 access path (per-Dag probes of
idx_dag_run_dag_id + one sort + WindowAgg), bounded regardless of where
each Dag's runs sit in time or what the planner estimates, while keeping
apache#67721's exact-N-per-Dag semantics. On run_after ties the window's
(id DESC) tiebreak deterministically keeps the latest-inserted row,
where the old per-branch LIMIT picked arbitrarily; the outer ORDER BY
leaves tied rows in unspecified order, same as the union. All columns
are selected inline (no join-back). dag_runs_limit is now NonNegativeInt,
matching limit/offset.

Measured end-to-end on a dockerized copy of a 3.8M-row production
metadata DB (PostgreSQL 15.14, default statistics, 74%-cold first page):

  GET /ui/dags?limit=50&dag_runs_limit=14:  31.2s -> 0.13-0.64s
  same rows and order returned (291/291 id-set match, total_entries
  unchanged); selection can differ from the union only on run_after ties,
  where the new pick is the newer run

Trade-off: the window sorts all runs of the page's Dags, so a page of the
50 highest-run-count Dags (~619K rows) costs ~2.0s with a 58MB external
sort (vs the union's 0.25-12s bistable plan) — linear and deterministic
instead of the 30-60s+ cold-page catastrophe.

The window-function shape is portable (same ANSI syntax as the
pre-apache#67721 rank() query and the existing row_number usage in
common/db/assets.py), so the sqlite/mysql/mssql matrix is unaffected.

Co-Authored-By: Claude Code <noreply@anthropic.com>
pierrejeambrun pushed a commit that referenced this pull request Oct 14, 2026
…ables (#74401)

* Fix /ui/dags recent-runs query degradation on large, skewed dag_run tables

The per-Dag UNION ALL branches introduced in #67721 plan as backward index
scans of the global idx_dag_run_run_after with a Filter on dag_id. On a
large, skewed dag_run table this is catastrophic for "cold" Dags — those
whose last run is months old or that never ran: the scan must walk
essentially every row of every *other* Dag before reaching that branch's
rows (measured on a 3.8M-row metadata DB: 3,800,261 rows removed by filter,
~2.3s for a single cold branch, 30-60s per page — gateway 504s), because
at default statistics the planner misestimates cold dag_ids (they fall
outside the MCV list) and bets the backward scan hits LIMIT matches
quickly. Hot pages are affected too, bistably: the identical statement
was observed flipping 22.8ms <-> 141s across a single ANALYZE.

Replace the 50-branch union with one page-scoped window query:

  row_number() OVER (PARTITION BY dag_id ORDER BY run_after DESC, id DESC)

over WHERE dag_id IN (page's dag ids), filtered to rn <= dag_runs_limit.
This restores the pre-#67721 access path (per-Dag probes of
idx_dag_run_dag_id + one sort + WindowAgg), bounded regardless of where
each Dag's runs sit in time or what the planner estimates, while keeping
#67721's exact-N-per-Dag semantics. On run_after ties the window's
(id DESC) tiebreak deterministically keeps the latest-inserted row,
where the old per-branch LIMIT picked arbitrarily; the outer ORDER BY
leaves tied rows in unspecified order, same as the union. All columns
are selected inline (no join-back). dag_runs_limit is now NonNegativeInt,
matching limit/offset.

Measured end-to-end on a dockerized copy of a 3.8M-row production
metadata DB (PostgreSQL 15.14, default statistics, 74%-cold first page):

  GET /ui/dags?limit=50&dag_runs_limit=14:  31.2s -> 0.13-0.64s
  same rows and order returned (291/291 id-set match, total_entries
  unchanged); selection can differ from the union only on run_after ties,
  where the new pick is the newer run

Trade-off: the window sorts all runs of the page's Dags, so a page of the
50 highest-run-count Dags (~619K rows) costs ~2.0s with a 58MB external
sort (vs the union's 0.25-12s bistable plan) — linear and deterministic
instead of the 30-60s+ cold-page catastrophe.

The window-function shape is portable (same ANSI syntax as the
pre-#67721 rank() query and the existing row_number usage in
common/db/assets.py), so the sqlite/mysql/mssql matrix is unaffected.

Co-Authored-By: Claude Code <noreply@anthropic.com>

* Add tests for recent-runs limit and run_after tie-breaking

Per review: one test asserts a Dag with more runs than dag_runs_limit
returns exactly the N newest in descending run_after order; one test
pins the (run_after DESC, id DESC) tie-break — with two runs tied on
run_after, the later-inserted run (higher id) is the deterministic pick,
which is the one spot the window query's output differs from the old
per-branch union.

Co-Authored-By: Claude Code <noreply@anthropic.com>

* Remove newsfragment per review

Co-Authored-By: Claude Code <noreply@anthropic.com>

* Trim redundant comments in the recent-runs tests

Co-Authored-By: Claude Code <noreply@anthropic.com>

* Keep dag_runs_limit as plain int

The NonNegativeInt validation is a contract change unrelated to the
regression this PR fixes — out of scope here.

Co-Authored-By: Claude Code <noreply@anthropic.com>

* Drop the explanatory comment above the ranked query

Co-Authored-By: Claude Code <noreply@anthropic.com>

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Oct 18, 2026
…ed dag_run tables (#74401) (#74966)

* Fix /ui/dags recent-runs query degradation on large, skewed dag_run tables

The per-Dag UNION ALL branches introduced in #67721 plan as backward index
scans of the global idx_dag_run_run_after with a Filter on dag_id. On a
large, skewed dag_run table this is catastrophic for "cold" Dags — those
whose last run is months old or that never ran: the scan must walk
essentially every row of every *other* Dag before reaching that branch's
rows (measured on a 3.8M-row metadata DB: 3,800,261 rows removed by filter,
~2.3s for a single cold branch, 30-60s per page — gateway 504s), because
at default statistics the planner misestimates cold dag_ids (they fall
outside the MCV list) and bets the backward scan hits LIMIT matches
quickly. Hot pages are affected too, bistably: the identical statement
was observed flipping 22.8ms <-> 141s across a single ANALYZE.

Replace the 50-branch union with one page-scoped window query:

  row_number() OVER (PARTITION BY dag_id ORDER BY run_after DESC, id DESC)

over WHERE dag_id IN (page's dag ids), filtered to rn <= dag_runs_limit.
This restores the pre-#67721 access path (per-Dag probes of
idx_dag_run_dag_id + one sort + WindowAgg), bounded regardless of where
each Dag's runs sit in time or what the planner estimates, while keeping
#67721's exact-N-per-Dag semantics. On run_after ties the window's
(id DESC) tiebreak deterministically keeps the latest-inserted row,
where the old per-branch LIMIT picked arbitrarily; the outer ORDER BY
leaves tied rows in unspecified order, same as the union. All columns
are selected inline (no join-back). dag_runs_limit is now NonNegativeInt,
matching limit/offset.

Measured end-to-end on a dockerized copy of a 3.8M-row production
metadata DB (PostgreSQL 15.14, default statistics, 74%-cold first page):

  GET /ui/dags?limit=50&dag_runs_limit=14:  31.2s -> 0.13-0.64s
  same rows and order returned (291/291 id-set match, total_entries
  unchanged); selection can differ from the union only on run_after ties,
  where the new pick is the newer run

Trade-off: the window sorts all runs of the page's Dags, so a page of the
50 highest-run-count Dags (~619K rows) costs ~2.0s with a 58MB external
sort (vs the union's 0.25-12s bistable plan) — linear and deterministic
instead of the 30-60s+ cold-page catastrophe.

The window-function shape is portable (same ANSI syntax as the
pre-#67721 rank() query and the existing row_number usage in
common/db/assets.py), so the sqlite/mysql/mssql matrix is unaffected.



* Add tests for recent-runs limit and run_after tie-breaking

Per review: one test asserts a Dag with more runs than dag_runs_limit
returns exactly the N newest in descending run_after order; one test
pins the (run_after DESC, id DESC) tie-break — with two runs tied on
run_after, the later-inserted run (higher id) is the deterministic pick,
which is the one spot the window query's output differs from the old
per-branch union.



* Remove newsfragment per review



* Trim redundant comments in the recent-runs tests



* Keep dag_runs_limit as plain int

The NonNegativeInt validation is a contract change unrelated to the
regression this PR fixes — out of scope here.



* Drop the explanatory comment above the ranked query



---------
(cherry picked from commit 1f70ead)

Co-authored-by: Ivan Tham <pickfire@riseup.net>
Co-authored-by: Claude Code <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Oct 20, 2026
…ed dag_run tables (#74401) (#74966)

* Fix /ui/dags recent-runs query degradation on large, skewed dag_run tables

The per-Dag UNION ALL branches introduced in #67721 plan as backward index
scans of the global idx_dag_run_run_after with a Filter on dag_id. On a
large, skewed dag_run table this is catastrophic for "cold" Dags — those
whose last run is months old or that never ran: the scan must walk
essentially every row of every *other* Dag before reaching that branch's
rows (measured on a 3.8M-row metadata DB: 3,800,261 rows removed by filter,
~2.3s for a single cold branch, 30-60s per page — gateway 504s), because
at default statistics the planner misestimates cold dag_ids (they fall
outside the MCV list) and bets the backward scan hits LIMIT matches
quickly. Hot pages are affected too, bistably: the identical statement
was observed flipping 22.8ms <-> 141s across a single ANALYZE.

Replace the 50-branch union with one page-scoped window query:

  row_number() OVER (PARTITION BY dag_id ORDER BY run_after DESC, id DESC)

over WHERE dag_id IN (page's dag ids), filtered to rn <= dag_runs_limit.
This restores the pre-#67721 access path (per-Dag probes of
idx_dag_run_dag_id + one sort + WindowAgg), bounded regardless of where
each Dag's runs sit in time or what the planner estimates, while keeping
#67721's exact-N-per-Dag semantics. On run_after ties the window's
(id DESC) tiebreak deterministically keeps the latest-inserted row,
where the old per-branch LIMIT picked arbitrarily; the outer ORDER BY
leaves tied rows in unspecified order, same as the union. All columns
are selected inline (no join-back). dag_runs_limit is now NonNegativeInt,
matching limit/offset.

Measured end-to-end on a dockerized copy of a 3.8M-row production
metadata DB (PostgreSQL 15.14, default statistics, 74%-cold first page):

  GET /ui/dags?limit=50&dag_runs_limit=14:  31.2s -> 0.13-0.64s
  same rows and order returned (291/291 id-set match, total_entries
  unchanged); selection can differ from the union only on run_after ties,
  where the new pick is the newer run

Trade-off: the window sorts all runs of the page's Dags, so a page of the
50 highest-run-count Dags (~619K rows) costs ~2.0s with a 58MB external
sort (vs the union's 0.25-12s bistable plan) — linear and deterministic
instead of the 30-60s+ cold-page catastrophe.

The window-function shape is portable (same ANSI syntax as the
pre-#67721 rank() query and the existing row_number usage in
common/db/assets.py), so the sqlite/mysql/mssql matrix is unaffected.



* Add tests for recent-runs limit and run_after tie-breaking

Per review: one test asserts a Dag with more runs than dag_runs_limit
returns exactly the N newest in descending run_after order; one test
pins the (run_after DESC, id DESC) tie-break — with two runs tied on
run_after, the later-inserted run (higher id) is the deterministic pick,
which is the one spot the window query's output differs from the old
per-branch union.



* Remove newsfragment per review



* Trim redundant comments in the recent-runs tests



* Keep dag_runs_limit as plain int

The NonNegativeInt validation is a contract change unrelated to the
regression this PR fixes — out of scope here.



* Drop the explanatory comment above the ranked query



---------
(cherry picked from commit 1f70ead)

Co-authored-by: Ivan Tham <pickfire@riseup.net>
Co-authored-by: Claude Code <noreply@anthropic.com>
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 ready for maintainer review Set by triage when all criteria pass, or by the fork CI reconciler when fork CI is green.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants