Airflow伦敦时区夏令时调度异常问题排查与解决
用户问题场景
用户的Airflow DAG代码如下:
from airflow import DAG import pendulum with DAG( dag_id="DLS_DAG", start_date = pendulum.datetime(2023, 10, 20, 5, tz="Europe/London"), schedule = '15,45 1 * * *', catchup=False, tags=["daylight_saving_testing"], )
该DAG原定每天伦敦时间1:15和1:45运行。2023年10月29日夏令时结束(时钟回拨1小时,出现两个1:00-2:00时段),任务触发了首次1:15、1:45的运行,还触发了重复时段的1:45运行,但跳过了重复时段的1:15运行。需求是跳过重复时段的两次运行,且必须使用伦敦时区,无法切换为UTC。
一、为何仅跳过重复时段的一次运行?
Airflow底层基于UTC时间计算调度间隔,所有本地时间都会转换为UTC进行跟踪。2023年10月29日伦敦夏令时结束时:
- 第一次1:15(BST,UTC+1)→ UTC 00:15
- 第一次1:45(BST,UTC+1)→ UTC 00:45
- 回拨后的1:15(GMT,UTC+0)→ UTC 01:15
- 回拨后的1:45(GMT,UTC+0)→ UTC 01:45
Airflow的调度逻辑会对比上一次运行的UTC时间,生成下一个符合cron规则的UTC时间。回拨后的1:15对应UTC 01:15,这个时间点落在前一次调度(UTC 00:45)到下一个预期调度(UTC 01:45)的间隔内,Airflow会判定该时间点属于“重复逻辑周期”,认为已处理过,所以跳过;而回拨后的1:45对应UTC 01:45,是下一个明确的UTC间隔点,因此被触发。
二、如何跳过重复时段的两次运行?
要完全跳过夏令时回拨的重复时段运行,最可靠的方式是自定义Timetable,在生成调度时间时过滤掉重复时段的时间点。以下是实现代码:
1. 定义夏令时安全的Timetable类
from airflow.timetables.base import DagRunInfo from airflow.timetables.interval import CronTimetable from pendulum import DateTime, timezone LONDON_TZ = timezone("Europe/London") class DSTSafeCronTimetable(CronTimetable): def next_dagrun_info( self, last_automated_dagrun: DateTime | None, restriction: DateTime | None, ) -> DagRunInfo | None: # 先调用父类获取默认下一个调度时间 dagrun_info = super().next_dagrun_info(last_automated_dagrun, restriction) if not dagrun_info: return None scheduled_local_time = dagrun_info.data_interval.start.in_timezone(LONDON_TZ) # 通用判断:识别夏令时回拨的重复时段 # 回拨后的时段特征:当前本地时间的DST为False,加1小时后DST为True one_hour_later = scheduled_local_time.add(hours=1) if scheduled_local_time.is_dst() is False and one_hour_later.is_dst() is True: # 跳过重复时段,递归获取下一个有效调度时间 return self.next_dagrun_info(scheduled_local_time, restriction) return dagrun_info
2. 在DAG中使用自定义Timetable
from airflow import DAG import pendulum with DAG( dag_id="DLS_DAG", start_date = pendulum.datetime(2023, 10, 20, 5, tz="Europe/London"), # 替换原schedule参数为自定义Timetable timetable=DSTSafeCronTimetable("15,45 1 * * *", timezone="Europe/London"), catchup=False, tags=["daylight_saving_testing"], ) as dag: # 此处添加你的任务定义 pass
这个Timetable会自动识别夏令时回拨的重复时段,跳过该时段内的所有调度点,完全符合需求。
三、三月夏令时调整时的任务触发情况
三月夏令时开始时,伦敦时间会从1:00直接跳至2:00,1:00-2:00的本地时段完全不存在。此时Airflow不会触发该时段的任务,因为这些本地时间无法转换为有效的UTC时间(时区转换会出现时间断裂),Airflow会直接跳过缺失的时段,调度下一个存在的有效时间点。
内容的提问来源于stack exchange,提问作者santanu basu

