Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The ``schedule="@continuous"`` parameter now works without requiring a ``start_date``, and any DAGs with this schedule will begin running immediately when unpaused.
10 changes: 4 additions & 6 deletions airflow-core/src/airflow/timetables/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,23 +147,21 @@ def next_dagrun_info(
last_automated_data_interval: DataInterval | None,
restriction: TimeRestriction,
) -> DagRunInfo | None:
if restriction.earliest is None: # No start date, won't run.
return None

current_time = timezone.coerce_datetime(timezone.utcnow())
start_date = restriction.earliest or current_time

if last_automated_data_interval is not None: # has already run once
if last_automated_data_interval.end > current_time: # start date is future
start = restriction.earliest
start = start_date
elapsed = last_automated_data_interval.end - last_automated_data_interval.start

end = start + elapsed.as_timedelta()
else:
start = last_automated_data_interval.end
end = current_time
else: # first run
start = restriction.earliest
end = max(restriction.earliest, current_time)
start = start_date
end = max(start_date, current_time)

if restriction.latest is not None and end > restriction.latest:
return None
Expand Down
20 changes: 18 additions & 2 deletions airflow-core/tests/unit/timetables/test_continuous_timetable.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,28 @@ def timetable():
return ContinuousTimetable()


def test_no_runs_without_start_date(timetable):
@time_machine.travel(DURING_DATE)
def test_runs_without_start_date(timetable):
next_info = timetable.next_dagrun_info(
last_automated_data_interval=None,
restriction=TimeRestriction(earliest=None, latest=None, catchup=False),
)
assert next_info is None
assert next_info is not None
assert next_info.run_after == DURING_DATE
assert next_info.data_interval.start == DURING_DATE
assert next_info.data_interval.end == DURING_DATE


@time_machine.travel(AFTER_DATE)
def test_subsequent_runs_without_start_date(timetable):
next_info = timetable.next_dagrun_info(
last_automated_data_interval=DataInterval(DURING_DATE, DURING_DATE),
restriction=TimeRestriction(earliest=None, latest=None, catchup=False),
)
assert next_info is not None
assert next_info.run_after == AFTER_DATE
assert next_info.data_interval.start == DURING_DATE
assert next_info.data_interval.end == AFTER_DATE


@time_machine.travel(DURING_DATE)
Expand Down