← 학습 카테고리

Learn

Airflow

151개 모듈 · 현재 47번째

Airflow 모듈 47/151 airflow-learn-47

Timetable 비교: Trigger vs Data Interval, catchup

Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation authoring-and-scheduling/timetable.rst - Timetables comparisons (Differences between trigger and data interval timetables, Differences between the cron and delta data interval timetables)

이 모듈을 다 읽으면

  • Trigger 계열과 Data Interval 계열이 data_interval_start/end를 다루는 방식 차이를 설명할 수 있다
  • catchup 설정이 재개된 Dag의 실행 개수와 logical_date에 미치는 영향을 판단할 수 있다
  • 동일한 cron 스케줄에서 두 타임테이블 계열이 만드는 run_id의 logical_date가 왜 다른지, 나아가 CronDataIntervalTimetable과 DeltaDataIntervalTimetable의 결과 차이를 예측할 수 있다
  • Trigger ↔ Data Interval 전환 시 실행이 스킵되는 조건과 이를 안정적으로 관리하는 방법을 판단할 수 있다

Trigger 계열과 Data Interval 계열 타임테이블은 같은 시각에 실행을 트리거하더라도 run_id의 logical_date를 다르게 계산하며, 이는 catchup 동작과 Dag 재개 시 스킵되는 실행 범위에 직접 영향을 준다. 두 계열을 전환할 때는 실행이 하나 스킵될 수 있는 조건도 있고, Cron과 Delta Data Interval 타임테이블은 start_date·catchup 조합에 따라 커버하는 인터벌이 서로 다르게 나뉜다.

Data Interval을 다루는 방식의 차이

Airflow는 cron·delta 스케줄에 대해 두 세트의 타임테이블을 제공한다 — ``CronTriggerTimetable``/``CronDataIntervalTimetable``은 둘 다 cron 표현식을 받고, ``DeltaTriggerTimetable``/``DeltaDataIntervalTimetable``은 둘 다 timedelta/relativedelta를 받는다. 이름의 'Trigger'와 'DataInterval'이 가리키듯, Trigger 타임테이블은 data interval 개념을 다루지 않아 ``data_interval_start``와 ``data_interval_end``가 같은 값(트리거된 시각)이 되고, Data Interval 타임테이블은 이 둘이 서로 다른 값을 가진다 — ``data_interval_end``는 트리거된 시각, ``data_interval_start``는 그 인터벌이 시작된 시각이다.

핵심 포인트

  • Trigger 타임테이블은 data_interval_start == data_interval_end(트리거된 시각)이고, Data Interval 타임테이블은 두 값이 서로 다르다(end=트리거 시각, start=인터벌 시작 시각)

catchup 동작과 재개된 Dag

``catchup``의 기본값은 ``False``다. 이는 두 시나리오에서 불필요한 Dag 실행을 막는다: (1) 과거 시점의 start_date로 새 Dag를 만들었을 때 — catchup=True라면 그 구간에 실행되었어야 할 모든 Dag run을 한꺼번에 실행하지만, False면 그러지 않는다. (2) 기존 Dag를 일시정지했다가 나중에 재개했을 때 — catchup=False면 정지 기간 동안 실행되었어야 할 run들을 실행하지 않는다. 이 두 시나리오 모두에서 run_id에 들어가는 ``logical_date``는 그 타임테이블이 data interval을 다루는 방식에 따라 결정된다. 기본 catchup 동작은 ``[scheduler] catchup_by_default`` 설정으로 바꿀 수 있다.

핵심 포인트

  • catchup 기본값은 False이며, 과거 start_date로 새 Dag를 만들 때와 일시정지 후 재개할 때 모두 불필요한 백필 실행을 막아준다
  • catchup 기본 동작은 [scheduler] catchup_by_default 설정으로 바꿀 수 있다

트리거 시점과 run_id의 logical_date

Trigger·Data Interval 두 계열 모두 같은 시각에 Dag run을 트리거하지만, ``run_id``에 들어가는 타임스탬프(``logical_date`` 기반)는 서로 다르다. 예를 들어 ``@daily``(``0 0 * * *``, 매일 자정) 스케줄을 1월 31일 오후 3시에 활성화한다고 하자.

* ``CronTriggerTimetable``은 2월 1일 자정에 새 Dag run을 만들고, run_id 타임스탬프도 2월 1일 자정이다. * ``CronDataIntervalTimetable``은 즉시 새 Dag run을 만든다 — 1월 31일 자정에 시작하는 일별 인터벌이 아직 실행되지 않았기 때문이다. run_id 타임스탬프는 그 인터벌의 시작인 1월 31일 자정이다.

실행을 건너뛰는 경우도 다르게 동작한다. 같은 ``@daily`` 스케줄을 쓰는 두 Dag를 1월 31일 오후 3시에 일시정지했다가 2월 2일 오후 3시에 재활성화한다고 하면:

* ``CronTriggerTimetable``은 2월 1일과 2일에 트리거되었어야 할 실행을 모두 건너뛰고, 다음 실행은 2월 3일 자정에 트리거된다. * ``CronDataIntervalTimetable``은 2월 1일 실행만 건너뛰고, 2월 2일 인터벌에 대한 실행은 재활성화 즉시 트리거된다.

이 예시들이 보여주듯, Trigger 타임테이블은 사람들이 워크플로에 기대하는 직관적인 방식으로 실행을 만들어내는 반면, Data Interval 타임테이블은 자신이 처리하는 데이터 인터벌 중심으로 동작할 뿐 워크플로 자체의 '고유한 속성'을 그대로 반영하지는 않는다.

핵심 포인트

  • 동일한 @daily 스케줄이라도 CronTriggerTimetable은 활성화 다음 트리거 시각(예: 다음날 자정)에 run_id를 매기고, CronDataIntervalTimetable은 즉시 실행하되 아직 처리 안 된 인터벌의 시작 시각에 run_id를 매긴다
  • 재개 시나리오에서도 CronTriggerTimetable은 정지 기간 중 모든 트리거를 건너뛰고 다음 예정 트리거부터 재개하지만, CronDataIntervalTimetable은 마지막 미처리 인터벌만 건너뛰고 그 다음 인터벌은 재개 즉시 실행한다

Trigger ↔ Data Interval 전환과 스위칭 메커니즘

두 계열은 ``logical_date``의 앵커가 다르다 — Trigger 타임테이블은 트리거 시각을, Data Interval 타임테이블은 ``data_interval_start``를 앵커로 쓴다. 기존 Dag run이 있는 Dag를 Trigger에서 Data Interval 타임테이블로 전환하면 실행이 하나 스킵된다 — 다음 실행이 이전 실행의 logical_date와 충돌하지 않도록 한 주기 앞으로 당겨지기 때문이다. 반대 방향(Data Interval → Trigger)은 실행을 스킵하지 않는다.

이 전환은 Dag 코드를 건드리지 않고도 두 가지 방식으로 일어날 수 있다: ``[scheduler] create_cron_data_intervals`` 설정을 바꾸면 ``schedule=``에 순수 cron 문자열을 쓰는 모든 Dag의 타임테이블 해석이 한꺼번에 바뀌고, 혹은 이 기본값이 다른 버전 경계를 넘을 때도 바뀐다 — Airflow 3는 기본값이 ``False``, Airflow 2.x는 기본값이 ``True``다. logical_date 의미론을 안정적으로 유지하려면 원하는 타임테이블을 미리 정해 고정해야 한다: 설정 플래그를 양쪽에서 동일한 값으로 명시하거나, ``schedule=``에 명시적인 타임테이블 인스턴스를 지정해 플래그 자체가 영향을 못 미치게 만드는 것이 방법이다.

핵심 포인트

  • Trigger에서 Data Interval로 전환하면 logical_date 충돌을 피하려고 다음 실행이 한 주기 앞당겨져 실행 하나가 스킵되지만, 반대 방향은 스킵되지 않는다
  • 이 전환은 [scheduler] create_cron_data_intervals 설정 변경이나 Airflow 2→3 버전 업그레이드(기본값이 True→False로 바뀜)로도 코드 변경 없이 발생할 수 있으므로, 플래그를 명시하거나 명시적 타임테이블 인스턴스를 써서 고정해야 한다

Cron vs Delta Data Interval Timetable 선택 기준

``DeltaDataIntervalTimetable``과 ``CronDataIntervalTimetable`` 중 어느 것을 쓸지는 상황에 따라 다르다. 2월 1일 01:05에 Dag를 활성화하는 시나리오로 비교하면:

cron ``*/30 * * * *``, start_date 2월 1일 00:00: catchup=True면 00:00-00:30과 00:30-01:00 두 인터벌이 모두 커버되고(timedelta와 동일 동작), catchup=False면 00:30-01:00만 커버된다. start_date가 00:10으로 늦춰지면, catchup=True여도 00:00-00:30 인터벌은 시작일 이후가 아니라서 스킵되고 00:30-01:00만 커버되며, catchup=False에서도 결과는 같다 — cron은 start_date와 무관하게 항상 시/일 등 캘린더 경계에 정렬되기 때문이다.

timedelta(minutes=30), start_date 2월 1일 00:00: catchup=True면 cron과 동일하게 00:00-00:30, 00:30-01:00을 커버하고, catchup=False면 00:35-01:05 하나만 커버된다(인터벌이 start_date가 아니라 현재 시각에 맞춰진다). start_date가 00:10이면, catchup=True에서는 00:10-00:40 인터벌이 만들어지고(start_date에 정렬, 다음 실행은 5분 뒤인 00:40-01:10을 커버) catchup=False에서는 00:35-01:05가 만들어진다(현재 시각에 정렬, 다음 실행은 30분 뒤).

요약하면, cron 기반 data interval은 항상 캘린더 경계(시/일 등)에 정렬되는 반면, timedelta 기반 data interval은 catchup=True일 때 start_date에, catchup=False일 때 활성화 시점(현재 시각)에 정렬된다.

핵심 포인트

  • cron data interval은 start_date나 catchup 값과 무관하게 항상 캘린더 경계(시/일 등)에 정렬된다
  • timedelta data interval은 catchup=True면 start_date에 정렬되고, catchup=False면 활성화 시점(현재 시각)에 정렬되어 인터벌 경계가 달라진다