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]

Reply via email to