Apache Airflow 1.10+;调度器支持在特定时间在不同的DST感知时区运行2个DAG吗?

Apache Airflow 1.10+;调度器支持在特定时间在不同的DST感知时区运行2个DAG吗?,airflow,airflow-scheduler,Airflow,Airflow Scheduler,Apache引入了对DST感知的本机支持 这使我想到(可能是错误的)应该可以在同一个气流调度器上创建2个DAG,如下所示: 每天06:00太平洋/奥克兰时间开始 每天从美国/纽约时间21:00开始 无需引入“睡眠”到所需开始时间的任务。该文档明确排除了用于DST感知调度的cron调度器,但仅解释了如何将DAG设置为每天在该时区(默认为午夜)运行 以前关于此主题的问题只考虑了使用or,其基础是没有引入对DST感知时区的本机支持 在“airflow.cfg”中,我将默认时区更新为系统时区。然后我

Apache引入了对DST感知的本机支持

这使我想到(可能是错误的)应该可以在同一个气流调度器上创建2个DAG,如下所示:

  • 每天06:00太平洋/奥克兰时间开始
  • 每天从美国/纽约时间21:00开始
无需引入“睡眠”到所需开始时间的任务。该文档明确排除了用于DST感知调度的cron调度器,但仅解释了如何将DAG设置为每天在该时区(默认为午夜)运行

以前关于此主题的问题只考虑了使用or,其基础是没有引入对DST感知时区的本机支持

在“airflow.cfg”中,我将默认时区更新为系统时区。然后我试着这样安排DAG:

DAG('NZ_SOD',
    description='New Zealand Start of Day',
    start_date=datetime(2018, 12, 11, 06, 00, tzinfo=pendulum.timezone('Pacific/Auckland')),
    catchup=False)
以及:

但是,似乎在Apache中没有明确考虑传递给
start\u date
的datetime对象的“时间”部分,并创建了意外的行为

气流是否有任何内置选项来产生所需的行为,或者我是否试图使用错误的工具进行作业?

首先是一些细节:

  • 不要使用前导0(如06 am)指定日期时间,因为如果匆忙将其编辑到上午9点,您将发现这不是有效的八进制数,整个DAG文件将停止解析
  • 您也可以使用钟摆符号:
    start\u date=be摆.datetime(2018,12,11,6,0,tz='Pacific/Auckland')
是的,气流中的时区有点混乱。假设cron时间表总是在该时区的偏移量内。这并不像应该的那样清楚,因为偏移量是不同的。假设您将默认配置时区设置为:

[core]
default_timezone = America/New_York
带有
开始日期
如:

start_date = datetime(2018, 12, 11, 6, 0),
UTC为
-18000
或-5h时,您将获得
偏移量

start_date = datetime(2018, 4, 11, 6, 0),
UTC为
-14400
或-4h时,您将获得
偏移量

其中,第二个项目符号中的偏移量为
46800
或13h,而4月份奥克兰的偏移量为
43200
或12h。如果我回忆正确,这些将应用于DAG的
计划间隔

文档似乎在说,您的
schedule\u interval
crontab字符串将永远在同一偏移量中解释。因此,
05***
如果你在12月份在纽约开始,那么它将在早上5点或6点运行;如果你在4月份在纽约开始,那么它将在早上5点或4点运行。UH我想那是对的。我对此也感到困惑

将默认值保留为utc并不能避免这种情况。不,如果您使用所示的
开始日期
并选择了与utc的偏移量不同的区域,则不会

现在…第二个问题,每天的时间。开始日期是有效的最早开始时间间隔。一天中的某个时间很好,但日程安排默认为
timedelta(days=1)
。我以为它是
@daily
,也就是
0***
,给你带来了有趣的结果,比如从12月11日上午6点开始,你的第一个完整的午夜到午夜间隔将在12月13日午夜结束,因此,第一次运行在12月12日午夜被传递为执行日期。但我预计,如果将
timedelta
应用于
开始日期
,它将在12月12日早上6点开始,昨天的时间与
执行日期
相同。然而,我没有看到它是这样工作的,这确实让我认为它可能只使用了
datetime
中的
datetime
部分作为
start\u date
的某个地方


如文件所述,传入的
执行日期
(以及所有宏日期)将以UTC为单位(因此,您的
开始日期
时区偏移中的午夜或早上6点转换为UTC)。至少它们附带了tz,因此如果必须,您可以在它们上使用
convert

答案是肯定的,cron时间表支持在支持DST的时区中运行DAG

但是有很多警告,所以我不得不假设气流的维护者没有将其作为一个受支持的用例。首先,截至撰写本报告时,当声明:

Cron调度

在设置cron计划的情况下,气流假设您始终希望在完全相同的时间运行。然后,它将忽略日光节约时间。因此,如果您有一个时间表,上面写着在每天08:00 GMT+1的时间间隔结束时运行,它将始终在08:00 GMT+1的时间间隔结束时运行,无论是否有日光节约时间

我编写了这段有点粗糙的代码,让您了解在不需要运行Airflow实例的情况下时间表是如何工作的(请注意,您安装了Penulum 1.x,如果您运行或编辑此代码,请使用正确的:

正如我们可以看到的,DST是使用cron计划观察到的,进一步说,如果您编辑我的代码以删除cron计划,您可以看到DST是未观察到的

但请注意,即使cron计划遵守DST,您可能仍然会在DST更改当天出现“按1天计算”错误,因为Airflow提供的是以前的日期,而不是当前日期(例如日历上的星期日,但在Airflow中,执行日期是星期六)。在我看来,这并不是在
follow\u计划中考虑的

最后,@dlamblin指出,如果DAG的本地执行日期与UTC执行日期不相同,则Airflow通过模板字符串或
provide_context=True
为Python可调用项提供的变量将是错误的。这可以通过使用
self来观察
start_date = datetime(2018, 4, 11, 6, 0),
import pendulum
from airflow import DAG
from datetime import timedelta


# Set-up DAG
test_dag = DAG(
    dag_id='foo',
    start_date=pendulum.datetime(year=2019, month=4, day=4, tz='Pacific/Auckland'),
    schedule_interval='00 03 * * *',
    catchup=False
)

# Check initial schedule
execution_date = test_dag.start_date
for _ in range(7):
    next_execution_date = test_dag.following_schedule(execution_date)
    if next_execution_date <= execution_date:
        execution_date = test_dag.following_schedule(execution_date + timedelta(hours=2))
    else:
        execution_date = next_execution_date
    print('Execution Date:', execution_date)
Execution Date: 2019-04-03 14:00:00+00:00
Execution Date: 2019-04-04 14:00:00+00:00
Execution Date: 2019-04-05 14:00:00+00:00
Execution Date: 2019-04-06 14:00:00+00:00
Execution Date: 2019-04-07 15:00:00+00:00
Execution Date: 2019-04-08 15:00:00+00:00
Execution Date: 2019-04-09 15:00:00+00:00
import datetime

def foo(*args, dag, execution_date, **kwargs):
    # Derive local execution datetime from dag and execution_date that
    # airflow passes to python callables where provide_context is set to True
    airflow_timezone = dag.timezone
    local_execution_datetime = airflow_timezone.convert(execution_date)

    # I then add 1 day to make it the calendar day
    # and not the execution date which Airflow provides
    local_cal_datetime = local_execution_datetime + datetime.timedelta(days=1)
# Standard Library
import datetime

# Third Party Libraries
import airflow.operators.email_operator
import airflow.operators.python_operator
import airflow.operators.bash_operator


class CustomTemplateVarsMixin:
    def render_template(self, attr, content, context):
        # Do Calculations
        airflow_execution_datetime = context['execution_date']
        airflow_timezone = context['dag'].timezone
        local_execution_datetime = airflow_timezone.convert(airflow_execution_datetime)
        local_cal_datetime = local_execution_datetime + datetime.timedelta(days=1)

        # Add to contexts
        context['local_cal_datetime'] = local_cal_datetime

        # Run normal Method
        return super().render_template(self, attr, content, context)


class BashOperator(CustomTemplateVarsMixin, airflow.operators.bash_operator.BashOperator):
    pass


class EmailOperator(CustomTemplateVarsMixin, airflow.operators.email_operator.EmailOperator):
    pass


class PythonOperator(CustomTemplateVarsMixin, airflow.operators.python_operator.PythonOperator):
    pass


class BranchPythonOperator(CustomTemplateVarsMixin, airflow.operators.python_operator.BranchPythonOperator):
    pass