이번 회사에서 Airflow를 처음 썼다. Airflow를 쓰면서 가장 이해하기 힘들었던 부분은 스케줄링과 Execution Date였다. 하지만 DE로서 여러 데이터 잡을 Airflow에서 운영해야하니 이해하지 않을 수 없었다. 중요한 daily DAG가 새벽에 돌다보니 Execution Date 때문에 실패해 잠에 깨거나 아침에 대참사를 맞이한 적도 종종 있었다. 😇 하지만 이렇게 이런저런 고생을 하다보니 이제는 어느정도 알게 됐다. 이해한 걸 정리도 하고 Airflow를 사용하시는 분들께 도움이 될 것 같아 공유해보려고 한다.
물론 2.2 이후부터는 Execution Date라는 표현을 사용하지 않고 Logical Date와 Data Interval이라는 표현을 쓴다. 하지만 아직 많은 곳에서 사용하기도 하고 UI에 표시도 되기 때문에 Execution Date로 지칭하고 표현을 혼용하겠다.
용어 정리
Schedule Interval
DAG가 어떤 주기로 수행돼야 하는지를 나타낸다. 0 4 * * *
처럼 cron 방식으로 표현된다.
Data Interval
execution_date
를 이해하기 위해서 매우 중요한 개념이다. 공식 문서에 따르면 “작동 시간 범위”를 나타낸다. DAG가 오늘 스케줄에서 처리해야할 데이터의 시간 범위이다. 예를 들어, 매일 자정에 스케줄되는 DAG의 data interval은 매일 자정에 시작해서 다음 날짜 자정에 끝난다. 예를 들어 2020–01–01의 데이터를 처리한다면 2020–01–02 00:00:00에 수행된다. 그림을 통해 좀 더 이해하고 싶다면 이 포스트를 추천한다.
Execution Date (Logical Date)
Data Interval의 시작 시간이다. 위의 2020–01–02 00:00:00에 수행되는 DAG에서는 2020–01–01 00:00:00이 Execution Date이다.
Start Date
실제로 시작하는 날짜가 아니다. DAG의 첫번째 Data Interval 시작 시간이다. 공식 문서에 따르면 start_date
에서 한 번의 Data Interval이 끝나고 DAG가 수행된다고 나와있다. DAG의 맨처음 수행과 Execution Date의 계산에 영향을 주는 값이다.
DAG Run
스케줄된 DAG의 인스턴스이다. DAG의 schedule_interval
이 정의되면 주기적으로 수행되거나 외부 트리거에 의해 수행되는데, 수행된 각각의 DAG가 DAG Run이 된다. 클래스랑 객체 관계랑 비슷한 것 같다.
Execution Date 계산 방식
DAG를 맨 처음 수행할 때와 이후로 방식이 나뉜다. 하지만 start_date
의 날짜와 시간은 모든 경우에서execution_date
의 계산에 영향을 주기 때문에 now와 같은 위험한 값은 사용하지 않는 걸 권장한다. 공식 문서에서도 동적인 값보다 고정된 값을 쓰는 걸 권장하고 있다. 또한 특별한 이유가 없다면 처음 설정한 이후 변경하지 않는 걸 권장한다.
이후 ‘이전schedule_interval
’ 이라는 표현을 굉장히 많이 사용한다. 정확히 정의하지 않으면 혼란을 줄 것 같아 미리 정의하고 가려고한다. 현재 시간을 기준으로 보면 이전 스케줄이 직전에 수행된 스케줄이고, 직전에 수행된 스케줄을 기준으로 보면 이전 스케줄이 그 이전의 스케줄이다. 즉, ‘이전 schedule_interval
’의 대상이 달라진다.
아래에서는 ‘이전 schedule_interval
’이라고 하면 현재 시간이 아닌 수행된 스케줄을 기준으로 한다. 공식적으로는 정확히 어떻게 지칭하는지 모르겠지만 schedule_interval
이라는 개념을 생각해보면 이렇게 표현하는 게 맞는 것 같아서 이렇게 표현한다.
DAG를 맨 처음 수행할 때
- DAG가 처음으로 언제 수행되는지와 원하는 스케줄 시간에 어떤
execution_date
가 설정되는지를 신경써야한다.
DAG는 언제 처음 수행되는가?
- DAG는 현재 시간이
start_date
에 지정된 날짜와 시간에서schedule_interval
까지 Data Interval을 설정할 수 있어야 수행된다. 그렇지 않다면 설정이 가능한 다음schedule_interval
에 수행된다. 따라서start_date
는 맨 처음 수행하길 원하는 날짜와 시간보다 무조건 이르게 설정해야한다. - 만약 배포후 DAG가 바로 수행되는 걸 원하지 않는다면
start_date
를 수행을 원하는 날짜와 시간에서 정확히 이전schedule_interval
로 하면된다. 바로 수행 돼도 괜찮다면 그 이전으로 하고 수동으로 성공 처리를 하면 된다. 이럴 경우 첫 수행을 버리고 다음 수행부터 원하는 대로 수행되길 바라는 것이다. - 예시를 보면 훨씬 이해하기가 쉬울 것 같다. DAG를 2023–06–11 04:00:00에 처음 제대로 수행하고 싶고 2023–06–10 17:00:00에 배포한다고 가정해보자. 아래와 같이 케이스를 분류할 수 있다.
execution_date
는 어떻게 계산되는가?
- execution_date = max(start_date의 날짜와 시간, start_date의 날짜와 현재 시간 및
schedule_interval
을 이용해 계산한 시간) - 자세한 계산 방식은 이 포스트에 나와있다. 하지만
start_date
혹은 이전schedule_interval
을execution_date
로 하면 된다. 계산 방식에도 나오듯이 DAG 인자로 넘겨준start_date
가 다른 값보다 크다면 그대로 쓰이니 주의해야한다. - 배포 후 DAG가 바로 수행됐다면 높은 확률로 원하는 시간에 원하는
execution_date
로 수행될 확률이 크다. - 실제 예시로 값을 계산해보면 다음과 같다.
DAG를 맨 처음 수행한 이후
- execution_date = max(start_date의 날짜와 시간, 마지막 DAG Run Data Interval End, 이전
schedule_interval
) - 역시 예시로 값을 계산해보자. 마지막 DAG Run이 오래전인 경우는 보통 스케줄을 꺼놓고 있다가 다시 키거나 이전 DAG Run들을 직접 삭제한 것이므로 스케줄 시간에 맞추지 않았다.
schedule_interval 변경시 주의 사항
운영을 하다보면 분명 schedule_interval
을 바꿔야하는 순간이 온다. 만약 해당 DAG에 의존하는 DAG가 존재해ExternalTaskSensor
를 사용한다면 execution_date
가 기대한 대로 설정되는게 중요하다. 센싱하는 DAG의 execution_date
가 틀리면 센싱이 실패하기 때문이다.
그래서 ExternalTaskSensor
를 사용해야한다면 아래 내용이 도움이 될 것 같다. 만약 DAG끼리 의존관계가 없어 센서를 사용하지 않거나 자동으로 execution_date
를 계산하게 만들었다면 이 부분은 스킵해도 괜찮다.
schedule_interval
을 변경하는 건 두 가지 경우가 있다. 시간을 미루거나 시간을 앞 당기는 것이다. 시간을 미룰 때는 배포하자마자 DAG가 수행된다. 그래서 다음 DAG Run에서는 execution_date
가 변경된 schedule_interval
에 맞춰서 설정된다.
시간을 앞당길 때는 마지막 DAG Run을 삭제하지 않으면, 다음 DAG Run에서 변경된 schedule_interval
에 맞게 execution_date
가 설정되지 않는다. 그래서 마지막 DAG Run을 삭제해야한다. 삭제하는 경우 시간을 미룰 때와 동일하게 삭제하자마자 DAG가 바로 수행되니 주의해야한다.
그림으로 예시를 들어보면 다음과 같다.
2023–06–10 17:00:00에 schedule_interval
을 30 4 * * *
으로 변경해 배포하면, 배포하자마자 노란색 Data Interval에 해당하는 DAG가 수행된다. 그리고 다음날인 2023–06–11부터는 Data Interval이 바뀐 schedule_interval
에 맞춰 2023–06–10 04:30:00 ~ 2023–06–11 04:30:00으로 설정된다.
2023–06–10 17:00:00에 schedule_interval
을 0 4 * * *
으로 변경해 배포하면, 배포해도 DAG가 수행되지 않는다. 이미 해당 Data Interval은 이전 스케줄로 커버됐기 때문이다. 마지막 DAG Run을 삭제하지 않으면, 다음 DAG Run인 2023–06–11에 수행될 DAG는 여기에 맞춰서 Data Interval이 2023–06–10 04:30:00 ~ 2023–06–11 04:00:00으로 설정된다. 삭제하면 노란색 Data Interval에 해당하는 DAG가 수행된다. 그러면 2023–06–11에 수행될 DAG는 Data Interval이 바뀐 schedule_interval
에 맞춰 2023–06–10 04:00:00 ~ 2023–06–10 04:00:00으로 설정된다.
UI로 다음 DAG Run 수행 시간 및 execution_date 확인하기
DAG 목록에서 혹은 DAG를 클릭해 들어갔을 때 우측 상단에서 다음 DAG Run의 수행 시간과 execution_date
를 확인할 수 있다. 각각 Run After
와 Data Interval
의 Start
이다. 특이한 예외가 있지 않은 이상 해당 값을 기반으로 수행된다. 그래서 배포후 아래 값들이 원하는 값으로 설정돼있는지 확인하는 걸 매우 권장한다.
정리
- DAG를 맨 처음 수행할 때
start_date
는 수행을 원하는 날짜와 시간에서 이전schedule_interval
에 정확히 맞추는 걸 권장한다. 처음 설정한 이후 변경하지 않는 게 좋다. - DAG의
execution_date
계산 방법은 맨처음과 이후로 나뉜다. 맨처음에는start_date
과schedule_interval
을 이용한다. 이후에는start_date
, 마지막 DAG Run의 Data Interval End,schedule_interval
을 이용한다. schedule_interval
을 변경할 때는 시간을 미루는 경우와 앞 당기는 경우로 나뉜다. 미루는 경우 배포하자마자 수행되고 다음 DAG Run부터execution_date
가 변경된 값을 기준으로 설정된다. 앞 당기는 경우 마지막 DAG Run을 삭제해야execution_date
가 변경된 값을 기준으로 설정된다. 마지막 DAG Run을 삭제하면 삭제시 바로 수행된다.- UI를 통해 다음 DAG Run의 수행 시간과
execution_date
를 확인할 수 있다.
Reference
- 공식 문서 첨부시 현재 사용하고 있는 2.2.2를 기준으로 첨부했다.
- https://airflow.apache.org/docs/apache-airflow/2.2.2/dag-run.html?highlight=execution_date#data-interval
- https://blog.bsk.im/2021/03/21/apache-airflow-aip-39/
- https://kaichu.se/Airflow/2020/08/16/Airflow-start-date-with-cron-schedule-interval-is-not-confused-anymore-when-you-know-this.html
- https://forum.astronomer.io/t/airflow-start-date-concepts/393
관련해서 이해하는데도 글을 작성하는데도 시간이 꽤 걸렸다. 분명 이해한 것 같다가도 경험하지 못한 케이스를 보면 너무 헷갈렸다. 아니면 잘못된 방식으로 오해하기도 했다. 사실 이 글을 작성하면서도 헷갈려 직접 테스트를 해보거나 여러 번 글을 수정했다. 그리고 이 개념을 잘 모르는 사람들에게 어떻게 설명해야 이해가 쉬울지 고민하다보니 더 고민이 많았던 것 같다. 이제 한 90%는 안 것 같은데, 또 다른 케이스가 나오면 다시 헷갈릴 것 같다. 😇 하지만 1년 전 처음 사용하던 때처럼 미지의 영역은 아니다. 이제라도 어느정도 파악해서 다행이라고 생각한다.