스케줄링과 backfill
Airflow 스케줄링의 핵심 규칙
Airflow 스케줄링에서 가장 많이 혼란을 야기하는 규칙이 하나 있다.
DAG 실행은 해당 데이터 구간(data interval)이 끝난 뒤에 시작된다.
@daily DAG의 start_date가 2025-01-01이면, 2025-01-01 00:00 ~ 2025-01-02 00:00 구간의 첫 실행은 2025-01-02 00:00 이후에 시작된다. 실행 버튼을 누른 시각이 아니라 처리 대상 데이터 구간을 기준으로 스케줄하기 때문이다.
이 원칙을 이해하면 나머지 개념들이 자연스럽게 풀린다.
schedule 파라미터
schedule에는 세 가지 형태를 쓸 수 있다.
# 1. cron 문자열
schedule="0 1 * * *" # 매일 새벽 1시
# 2. cron 프리셋
schedule="@daily" # 매일 자정
schedule="@hourly" # 매시 정각
schedule="@weekly" # 매주 일요일 자정
# 3. timedelta
from datetime import timedelta
schedule=timedelta(hours=6) # 6시간마다| 프리셋 | 동등한 cron |
|---|---|
@once | 한 번만 실행 |
@hourly | 0 * * * * |
@daily | 0 0 * * * |
@weekly | 0 0 * * 0 |
@monthly | 0 0 1 * * |
@yearly | 0 0 1 1 * |
data_interval과 logical_date
Airflow 2.2 이전에는 execution_date라는 개념이 있었다. 이름 때문에 "실행 시각"으로 오해하기 쉬웠고, 실제로는 데이터 구간의 시작점을 뜻했다. Airflow 2.2부터 더 명확한 이름으로 바뀌었다.
data_interval_start: 이 DAG 실행이 담당하는 데이터 구간의 시작data_interval_end: 데이터 구간의 끝logical_date:data_interval_start와 동일 (execution_date의 새 이름)
2025-01-01
2025-01-02
2025-01-02 00:00+
2025-01-02
2025-01-03
2025-01-03 00:00+
Task 코드에서 이 값을 참조하려면 Jinja 템플릿 변수를 쓴다.
BashOperator(
task_id="load",
bash_command="python load.py --date {{ data_interval_start | ds }}",
){{ data_interval_start | ds }}는 2025-01-01 형식의 날짜 문자열로 렌더링된다.
catchup — 과거 구간 자동 보완
DAG를 새로 만들거나, 오랫동안 일시정지했다가 다시 켰을 때 start_date와 현재 시각 사이에 실행되지 않은 구간이 쌓여 있을 수 있다. catchup 파라미터가 이 동작을 제어한다.
with DAG(
dag_id="daily_report",
schedule="@daily",
start_date=datetime(2025, 1, 1),
catchup=True, # 기본값: 미실행 구간 모두 채운다
) as dag:
...catchup=True(기본): 스케줄러가 미실행 구간을 모두 DAG Run으로 생성해 순차적으로 실행한다.catchup=False: 가장 최근 구간 하나만 실행한다. 과거 이력은 무시.
운영 환경에서
catchup=True를 기본으로 두면 DAG를 재배포할 때 예상치 못한 폭발적 실행이 생길 수 있다. 대부분의 팀은catchup=False를 기본으로 설정하고, 필요할 때만 backfill을 수동으로 실행한다.
backfill — 수동 과거 구간 재실행
Catchup이 스케줄러의 자동 동작이라면, backfill은 사용자가 명시적으로 특정 날짜 범위를 재실행하도록 요청하는 작업이다.
언제 쓰나
- 파이프라인 버그를 수정한 뒤 영향받은 기간을 재처리할 때
- 새 DAG를 배포하면서 과거 데이터를 소급 처리할 때
- 다운타임 복구 후 누락 구간을 채울 때
CLI backfill
Airflow 2.x에서는 CLI로 backfill을 실행했다.
# 2025년 1월 한 달치 재실행
airflow dags backfill \
--start-date 2025-01-01 \
--end-date 2025-01-31 \
daily_report
# 실제 실행 전 어떤 구간이 생성되는지 확인 (dry-run)
airflow dags backfill \
--start-date 2025-01-01 \
--end-date 2025-01-31 \
--dry-run \
daily_reportAirflow 3.0 변화: Scheduler-Managed Backfill
Airflow 3.0부터 backfill은 별도 CLI 프로세스가 아니라 Scheduler가 직접 관리하는 방식으로 바뀌었다. 이로 인해 backfill 실행도 일반 DAG Run과 동일한 스케줄링·버전 관리·모니터링 흐름을 따른다.
# Airflow 3.x: backfill 생성 (Scheduler가 처리)
airflow dags backfill create --dag-id daily_report \
--from-date 2025-01-01 --to-date 2025-01-31catchup vs backfill 비교
미실행 구간 전체
자동 생성
선택적 재실행
(버그 수정·소급처리)
| 항목 | catchup | backfill |
|---|---|---|
| 트리거 | 자동 (Scheduler) | 수동 (CLI / UI) |
| 범위 | start_date ~ 현재 미실행 전체 | 지정 날짜 범위 |
| 주 용도 | 재시작 후 연속성 보장 | 버그 수정 후 재처리 |
| 제어 | catchup=True/False | --start-date / --end-date |
max_active_runs — 동시 실행 제한
catchup이 활성화되면 수십 개의 DAG Run이 동시에 실행될 수 있다. max_active_runs로 동시 실행 수를 제한해 리소스 과부하를 방지한다.
with DAG(
dag_id="daily_report",
schedule="@daily",
start_date=datetime(2025, 1, 1),
catchup=True,
max_active_runs=3, # 동시에 최대 3개 DAG Run만
) as dag:
...References
- https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html
- https://airflow.apache.org/blog/airflow-three-point-oh-is-here/
- https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timetable.html
- https://medium.com/@su-paris/apache-airflow-4-scheduling-and-catchup-3d733f92a2bc
- https://faun.pub/airflow-catchup-vs-backfill-22f3a7d4ba6f