Dag Run과 데이터 인터벌
Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation core-concepts/dag-run.rst - Dag Run Status, Data Interval, Manual Triggering and Data Intervals
이 모듈을 다 읽으면
- Dag Run의 상태가 'leaf node'들의 상태로부터 어떻게 결정되는지 설명할 수 있다
- trigger timetable과 data-interval timetable에서 data_interval_start/end, logical_date가 각각 어떻게 다르게 정의되는지 설명할 수 있다
- 수동 트리거 시 data_interval이 logical_date와 자동으로 같아지지 않는다는 점을 설명할 수 있다
Dag Run은 특정 시점에 Dag가 인스턴스화된 것을 나타내는 객체로, Dag가 실행될 때마다 새로 생성되며 여러 Dag Run이 동시에 병렬로 실행될 수 있다. 이 모듈은 Dag Run의 상태가 leaf node들로부터 어떻게 결정되는지, Airflow 3에서 데이터 인터벌 개념이 트리거 타임테이블과 데이터 인터벌 타임테이블 두 갈래로 나뉘는 방식, 그리고 logical_date/data_interval/start_date의 관계를 다룬다.
Dag Run 상태는 leaf node로 결정된다
Dag Run의 상태는 Dag 실행이 끝났을 때 결정되며, Dag의 실행은 포함된 태스크들과 그 의존성에 좌우된다. 모든 태스크가 터미널 상태(success, failed, skipped 등 더 이상 다른 상태로 전이될 수 없는 상태) 중 하나가 되었을 때 Dag Run에 상태가 부여된다. 이때 기준이 되는 것은 '리프 노드(leaf node)' — 자식이 없는 태스크들 — 의 상태다. 리프 노드가 모두 success나 skipped면 Dag Run은 success, 리프 노드 중 하나라도 failed나 upstream_failed면 Dag Run은 failed가 된다.
여기서 주의할 점은 특정 trigger rule을 가진 태스크가 있으면 예상치 못한 결과가 나올 수 있다는 것이다. 예를 들어 trigger_rule이 ``all_done``인 리프 태스크가 있다면, Dag의 다른 부분에서 무언가 실패했더라도 그 리프 태스크가 성공하면 Dag Run 전체가 success로 표시될 수 있다. Airflow 2.7부터는 현재 실행 중인 Dag Run이 있는 Dag를 UI 대시보드의 'Running' 탭에서, 가장 최근 실행이 failed인 Dag를 'Failed' 탭에서 볼 수 있다.
핵심 포인트
- Dag Run의 최종 상태는 자식이 없는 '리프 노드' 태스크들의 상태로 결정된다 — 모두 success/skipped면 success, 하나라도 failed/upstream_failed면 failed
- trigger_rule이 all_done인 리프 태스크가 성공하면, 중간에 다른 태스크가 실패했더라도 Dag Run 전체가 success로 표시될 수 있으므로 주의가 필요하다
데이터 인터벌: 트리거 타임테이블 vs 데이터 인터벌 타임테이블
각 Dag Run은 자신이 다루는 시간 범위를 나타내는 '데이터 인터벌(data interval)'을 갖는데, 이 인터벌이 정의되는 방식은 Dag의 타임테이블에 따라 다르다. Airflow 3에서는 ``@daily`` 같은 순수 cron 문자열로 스케줄링된 Dag가 기본적으로 CronTriggerTimetable을 사용한다(``[scheduler] create_cron_data_intervals``가 기본값 False). 이 타임테이블에서는 ``data_interval_start``와 ``data_interval_end``가 같은 값 — 트리거된 시각(``@daily``라면 매일 자정) — 이며, Dag Run은 바로 그 시각에 실행되도록 생성된다.
만약 자정부터 다음 자정까지처럼 연속된 하루 전체를 다루는 윈도우가 필요하다면, CronDataIntervalTimetable 같은 데이터 인터벌 타임테이블을 쓰거나 ``[scheduler] create_cron_data_intervals``를 True로 설정해야 한다. 이 경우 Dag Run은 보통 해당 데이터 인터벌이 끝난 '이후'에 스케줄링되므로, 2020-01-01을 다루는 실행은 일반적으로 2020-01-02 00:00:00 이후에나 시작된다.
'논리적 날짜(logical date, Airflow 2.2 이전에는 execution_date)'는 두 타임테이블 종류 모두에서 ``data_interval_start``로 정의된다. 기본(제로 폭) 트리거 타임테이블에서는 이것이 트리거 시각과 같고, interval이 0이 아닌 트리거 타임테이블에서는 ``트리거 시각 - interval``이 되며, 데이터 인터벌 타임테이블에서는 연속 윈도우의 시작 시각(실제 실행 시각이 아님)이 된다. Dag와 태스크의 ``start_date`` 인자는 태스크가 실행을 시작하는 시각이 아니라, 스케줄러가 생성할 가장 이른 '트리거 시각(run_after)'을 나타낸다 — 데이터 인터벌 타임테이블에서는 start_date가 첫 데이터 인터벌의 시작이기도 하므로, 첫 실행은 그 윈도우가 닫히는 한 스케줄 주기 이후에나 실행된다.
핵심 포인트
- Airflow 3의 기본 CronTriggerTimetable에서는 data_interval_start와 data_interval_end가 동일하며 트리거된 시각을 가리킨다
- 연속된 전체 기간(예: 자정~다음 자정)을 다루려면 CronDataIntervalTimetable 또는 create_cron_data_intervals=True를 써야 하며, 이 경우 실행은 인터벌이 끝난 이후에 시작된다
- logical_date는 항상 data_interval_start와 같지만, start_date는 스케줄러가 실행을 생성하는 가장 이른 트리거 시각(run_after)일 뿐 실제 태스크 시작 시각이 아니다
수동 트리거와 데이터 인터벌의 불일치
CLI, REST API, UI, 또는 ``TriggerDagRunOperator``로 Dag를 수동 트리거할 때, 그 실행의 ``data_interval``이 사용자가 넘긴 ``logical_date``로부터 자동으로 유도되거나 그와 같다고 가정해서는 안 된다. 스케줄된 실행에서는 타임테이블이 데이터 인터벌을 직접 정의하지만, 수동 트리거된 실행에서는 결과 ``data_interval``이 타임테이블과 트리거 경로에 따라 달라지며 그 실행의 ``logical_date``와 다를 수 있다. Dag 로직에서 수동 실행 시 사용자가 지정한 날짜가 필요하다면, ``data_interval_start``나 ``data_interval_end``와 같다고 가정하지 말고 ``logical_date``를 명시적으로 사용해야 한다.
핵심 포인트
- 수동 트리거된 Dag Run의 data_interval은 logical_date로부터 자동으로 유도되지 않으며 둘이 다를 수 있다
- 수동 실행에서 사용자가 지정한 날짜가 필요하면 data_interval_start/end 대신 logical_date를 명시적으로 참조해야 한다