Lee-W commented on code in PR #71072:
URL: https://github.com/apache/airflow/pull/71072#discussion_r4256565503
##########
airflow-core/src/airflow/jobs/scheduler_job_runner.py:
##########
@@ -2395,8 +2421,96 @@ def _create_dagruns_for_partitioned_asset_dags(self,
session: Session) -> set[st
)
).all()
if not pending_apdrs:
+ # Backlog fully drained: the next tick that re-crosses the cap is
a new episode,
+ # not a continuation of whatever was reported before.
+ self._partition_cap_backlog_reported = False
Review Comment:
An empty fetch no longer ends the episode by itself. When
`_partition_cap_backlog_reported` is set, the empty branch runs then unlocked
count and clears the flag only when it is at or below the cap (same rule as the
non-empty path). No extra query when no episode is open. Added a test that
simulates an empstill above the cap and checks the next full fetch logs at
debug with a single audit row.Z
##########
airflow-core/src/airflow/jobs/scheduler_job_runner.py:
##########
@@ -2395,8 +2421,96 @@ def _create_dagruns_for_partitioned_asset_dags(self,
session: Session) -> set[st
)
).all()
if not pending_apdrs:
+ # Backlog fully drained: the next tick that re-crosses the cap is
a new episode,
+ # not a continuation of whatever was reported before.
+ self._partition_cap_backlog_reported = False
return set()
+ sorted_dag_ids: list[str] = []
+ if (
+ len(pending_apdrs) >= self._max_partition_dag_runs_per_loop
+ or self._partition_cap_backlog_reported
+ ):
+ # A full fetch alone can't tell us whether that's the entire
backlog or just
+ # this tick's slice of a larger one, and a short fetch can't
either while other
+ # schedulers hold row locks, so we only pay for this query on a
full fetch or
+ # while an episode is being tracked.
+ # Per-Dag counts across the *whole* backlog, not just this tick's
oldest-cap
+ # slice (`pending_apdrs`) — a Dag whose partitions haven't reached
the front of
+ # the FIFO queue yet would otherwise be missing from the log/audit
row until its
+ # turn comes up.
+ backlogs_per_dag: dict[str, int] = {
+ dag_id: count
+ for dag_id, count in session.execute(
+ select(AssetPartitionDagRun.target_dag_id, func.count())
+ .select_from(AssetPartitionDagRun)
+ .join(DagModel, DagModel.dag_id ==
AssetPartitionDagRun.target_dag_id)
+ .where(*_build_pending_partitioned_apdr_filters())
+ .group_by(AssetPartitionDagRun.target_dag_id)
+ )
+ }
+ backlog_total = sum(backlogs_per_dag.values())
+ # No SQL-side ORDER BY: it would have no dag_id tie-break, and
relying on dict
+ # insertion order mirroring DB row order across backends (SQLite
in tests vs
+ # Postgres/MySQL in production) isn't a contract worth trusting —
sort explicitly.
+ sorted_dag_ids = sorted(backlogs_per_dag, key=lambda dag_id:
(-backlogs_per_dag[dag_id], dag_id))
+ if backlog_total <= self._max_partition_dag_runs_per_loop:
+ self._partition_cap_backlog_reported = False
+ else:
+ backlog_total = len(pending_apdrs)
+
+ if backlog_total > self._max_partition_dag_runs_per_loop:
+ displayed_dag_ids =
sorted_dag_ids[:MAX_PARTITION_CAP_BACKLOG_DAG_IDS_LOGGED]
+ remaining_dag_id_count = len(sorted_dag_ids) -
len(displayed_dag_ids)
+ log_cap_reached = self.log.debug if
self._partition_cap_backlog_reported else self.log.warning
+ log_cap_reached(
+ (
+ "Reached the per-tick cap on pending partitioned Dag runs;
the remaining backlog "
+ "will be evaluated over subsequent scheduler ticks"
Review Comment:
Reworded both the log event and the audit `extra`. They now say only the
oldest `cap` pending partitioned Dag runs are evaluated per tick and newer ones
wait behind them. Removed "evaluated" or "eligible" from the audit text.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]