dag_maker adopts upstream's deterministic run defaults¶
Issue #259 (the #227 round-3 scan) measured the residual dag_maker drift at ~200+
upstream-test failures per certified version, on both families, and traced it to three
defaults this plugin chose differently from upstream tests_common:
- No default Dag
start_date: upstream injects one (explicit kwarg >default_args["start_date"]> the consumer test module'sDEFAULT_DATEattribute >2016-01-01UTC), and upstream suites assert ontask.start_dateand build run dates from it -- a flat 49-50 failures/version were exactlyassert task.start_date is not None. logical_datedefaulted toutcnow(): a run dated today falls outside a Dag whosestart_date/end_datewindow is upstream's 2016-vintageDEFAULT_DATE, soverify_integritycreated ZERO task instances -- the emptydr.task_instancesclass.- Derived run ids spelled
manual__pytest-airflow-in-a-box-<dag_id>-<hash>: upstream defaults totest(norun_typepassed) or the timetable-generated id (explicitrun_type), and upstream tests look runs up by those.
ADR 0002 kept the hashed run id on purpose, calling it "the xdist collision mitigation
for same-dag_id contention". That claim does not hold up: _default_run_id(dag_id,
invocation) never encoded worker identity, and DagRun uniqueness is per (dag_id,
run_id) anyway -- collision safety always came entirely from the derived per-test
dag_id, never from the run-id spelling. A fixed test id under a unique dag_id
collides with nothing; under a caller-chosen repeated dag_id the contention exists
regardless of spelling and remains the documented xdist_group caveat.
We decided to adopt upstream's defaults wholesale in dag_maker:
dag_maker(...)injects a defaultstart_datevia upstream's ladder, exposed asdag_maker.start_date. One deliberate deviation: an EXPLICITstart_date=Noneopts out of injection entirely (upstream silently replaces it withDEFAULT_DATE); only an absent key climbs the ladder. A moduleDEFAULT_DATEthat is not adatetimeis ignored rather than handed to Airflow's constructor to blow up far from its source.create_dagrun()withoutlogical_dateuses the Dag's resolvedstart_datefor manual runs and the timetable's next-run info for explicit non-manualrun_types (next_dagrun_info-- keywordlast_automated_run_infoon 3.2+,last_automated_dagrunbefore), degrading tostart_datethenutcnow()when the timetable schedules nothing (schedule=None) or the Dag opted out ofstart_date-- where upstream would crash on theNonerun info.create_dagrun()withoutrun_idusestestwhen norun_typewas passed, andtimetable.generate_run_id(...)when one was -- keyed on the KEYWORD's presence, so an explicitrun_type=MANUALalso gets a generated id, exactly as upstream.data_intervalinference follows the run type: manual runs keepinfer_manual_data_interval; non-manual runs use the interval carried by the timetable's own run info when the date came fromnext_dagrun_info, andinfer_automated_data_intervalotherwise (module-level inairflow.models.dagon 3.1+, the instance method on 2.x). That function whitelists cron/delta/null/once shapes and hard-raisesNot a valid timetablefor trigger-style and custom timetables -- a second deliberate deviation degrades it to the manual inference every timetable implements, instead of importing upstream's crash into this plugin's first-class custom-timetable support. The run's defaultstart_dateandrun_afteralso take upstream's shapes (the Dag'sstart_date, and the resolved interval's end).
run_dag keeps its derived manual__pytest-airflow-in-a-box-... ids and utcnow()
dating for EVERY run type: it has no upstream analogue and adopts externally-authored
Dags whose start_date this plugin does not control -- silently backdating such a run
by years would change depends_on_past, sensor windows, and {{ ds }} semantics. The
timetable derivation is therefore gated behind create_dag_run's
upstream_defaults flag, which only dag_maker sets.
Consequences: this supersedes ADR 0002's "run-id conventions stay divergent" clause --
the rest of 0002 (the authoring yield, the opt-in scheduler handles) stands. A second
bare create_dagrun() on one Dag now collides loudly on (dag_id, "test") exactly as
upstream does, where the invocation-counter ids used to make it silently unique -- pass
explicit run_ids for multi-run tests. And every Dag dag_maker builds now carries a
start_date unless the test explicitly passes start_date=None.