jedcunningham opened a new pull request, #66132:
URL: https://github.com/apache/airflow/pull/66132
## What's the bug
`_DataIntervalTimetable.next_dagrun_info` aligns the next run's `start` onto
`data_interval.end` of the previous run. When that previous interval is
zero-length (`start == end`), `start` lands on the *same* point as the existing
run's `logical_date`, so the scheduler tries to create a run that collides with
the existing one and logs:
```
run already exists; skipping dagrun creation
```
It then calls
`calculate_dagrun_date_fields(last_automated_run=existing_run)` to advance —
which feeds the same zero-length interval back in and recomputes the same
value. The scheduler loops on the skip warning until the next cron boundary
nudges `_skip_to_latest` to a different result, at which point the gate may
have already rolled past the slot — so the intended run is silently skipped.
## When does this happen?
Zero-length `data_interval`s are the normal output of `CronTriggerTimetable`
(point-in-time) but never of `CronDataIntervalTimetable` (windowed, validated
`> 0`). They appear on a `CronDataIntervalTimetable` DAG when its scheduling
history was produced by `CronTriggerTimetable` and the DAG later switches to
`CronDataIntervalTimetable` — for example, when `[scheduler]
create_cron_data_intervals` is flipped from `False` (Airflow 3 default) to
`True` after some runs have already been created.
## The fix
In `_DataIntervalTimetable.next_dagrun_info`, after computing `start`, if
the previous interval was zero-length and `start` falls on it, advance `start`
by one period so the new run gets a fresh `logical_date`.
The guard only fires for `_DataIntervalTimetable` subclasses
(`CronDataIntervalTimetable`, `DeltaDataIntervalTimetable`).
`CronTriggerTimetable` / `DeltaTriggerTimetable` extend a separate
`_TriggerTimetable` base with its own `next_dagrun_info` and never go through
this code path, so their normal zero-length output is unaffected.
## Tests
Added `test_zero_length_last_interval_does_not_re_emit_logical_date`
parametrized on `catchup ∈ {True, False}` — both branches converge on the same
expected interval, demonstrating the guard absorbs the difference between the
two paths into the previous-run handling.
related: #59618
##### Was generative AI tooling used to co-author this PR?
- [X] Yes — Claude Code (Anthropic)
<!-- Generated-by: Claude Code following [the
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
-->
--
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]