Airflow 气流:带ExternalTaskSensor的Master Dag永远卡住

Airflow 气流:带ExternalTaskSensor的Master Dag永远卡住,airflow,Airflow,要求一个接一个地运行DAG,并在每个DAG成功后运行 我有一个主DAG,我在其中调用所有DAG,以便按顺序一个接一个地执行 此外,在每个dag_A、dag_B、dag_C中,我必须给定schedule_interval=None并在GUI中手动打开 我正在使用ExternalTaskSensor,因为在第一个dag_A中的所有任务完成之前,它就启动了第二个dag_B,为了避免此类问题,我正在使用ExternalTaskSensor。如果有更好的实现,请告诉我 不知道我错过了什么 代码:maste

要求一个接一个地运行DAG,并在每个DAG成功后运行

我有一个主DAG,我在其中调用所有DAG,以便按顺序一个接一个地执行

此外,在每个dag_A、dag_B、dag_C中,我必须给定schedule_interval=None并在GUI中手动打开

我正在使用ExternalTaskSensor,因为在第一个dag_A中的所有任务完成之前,它就启动了第二个dag_B,为了避免此类问题,我正在使用ExternalTaskSensor。如果有更好的实现,请告诉我

不知道我错过了什么

代码:master_dag.py

import datetime
import os
from datetime import timedelta

from airflow.models import DAG, Variable

from airflow.operators.dagrun_operator import TriggerDagRunOperator
from airflow.operators.sensors import ExternalTaskSensor

default_args = {
        'owner': 'airflow',
        'start_date': datetime.datetime(2020, 1, 7),
        'provide_context': True,
        'execution_timeout': None,
        'retries': 0,
        'retry_delay': timedelta(minutes=3),
        'retry_exponential_backoff': True,
        'email_on_retry': False,
    }


dag = DAG(
        dag_id='master_dag',
        schedule_interval='7 3 * * *',
        default_args=default_args,
        max_active_runs=1,
        catchup=False,
    )

trigger_dag_A = TriggerDagRunOperator(
    task_id='trigger_dag_A',
        trigger_dag_id='dag_A',
        dag=dag,
    )

wait_for_dag_A = ExternalTaskSensor(
    task_id='wait_for_dag_A',
    external_dag_id='dag_A',
    external_task_id='proc_success',
    poke_interval=60,
    allowed_states=['success'],
    dag=dag,
    )

trigger_dag_B = TriggerDagRunOperator(
        task_id='trigger_dag_B',
        trigger_dag_id='dag_B',
        dag=dag,
    )

wait_for_dag_B = ExternalTaskSensor(
    task_id='wait_for_dag_B',
    external_dag_id='dag_B',
    external_task_id='proc_success',
    poke_interval=60,
    allowed_states=['success'],
    dag=dag)

trigger_dag_C = TriggerDagRunOperator(
        task_id='trigger_dag_C',
        trigger_dag_id='dag_C',
        dag=dag,
    )

trigger_dag_A >> wait_dag_A >> trigger_dag_B >> wait_dag_B >> trigger_dag_C
每个DAG都有多个正在运行的任务,最后一个任务已成功运行

后台

  • 分别轮询外部
    DAG
    任务的
    DagRun
    /
    任务实例
    的状态(基于是否传递了
    外部任务id
  • 现在,由于单个
    DAG
    可以有多个活动
    DagRun
    s,因此必须告知传感器应该感测哪些运行/实例
  • 为此,它使用执行日期作为区分标准。这可以(仅)用以下两种方式之一表示

您的实现中的问题

  • ExternalTaskSensor
    s中,您没有传递
    execution\u date\u fn
    execution\u delta
    参数
  • 因此,要轮询子级
    DAG
    s的
    DagRun
    s的传感器会被卡住(显然,父级/协调器DAG的
    执行日期与子级DAG不同)

进一步提示

  • 您可以跳过传递
    外部任务\u id
    ;执行此操作时,
    ExternalTaskSensor
    实际上将成为
    ExternalDagSensor
    。当您的子DAG(A、B和C)有多个结束任务时,这尤其有用(因此,完成其中任何一个结束任务都不能保证完成整个DAG)
  • 还可以查看此讨论:


编辑-1

事后一想,我最初的判断似乎是错误的;尤其是下面的陈述不成立

显然,您的家长/编曲人DAG的执行日期是 与儿童DAG不同

查看,很明显,
TriggerDagRunOperator
将自己的
执行日期
传递给子
DagRun
,这意味着
ExternalTaskSensor
应该能够感测到该DAG或其任务

 trigger_dag(
            dag_id=self.trigger_dag_id,
            run_id=run_id,
            conf=self.conf,
            # own execution date passed to child DAG
            execution_date=self.execution_date,
            replace_microseconds=False,
        )
因此,这种解释并不成立

我建议你去

  • 在UI中或通过查询meta db,检查您所触发的子DAG/您正在传递的
    外部任务id
    的任务的
    执行日期
  • 并将其与编排器DAG的执行日期进行比较

这将澄清某些细节

感谢您的详细解释,感谢您的支持response@Kar如果您能够解决这个问题,请考虑将解决方案添加到这里作为对其他人的回答。
:param execution_date_fn: function that receives the current execution date
    and returns the desired execution dates to query. Either execution_delta
    or execution_date_fn can be passed to ExternalTaskSensor, but not both.
:type execution_date_fn: callable
@provide_session
def poke(self, context, session=None):
    if self.execution_delta:
        dttm = context['execution_date'] - self.execution_delta
    elif self.execution_date_fn:
        dttm = self.execution_date_fn(context['execution_date'])
    else:
        # if neither of above is passed, use current DAG's execution date
        dttm = context['execution_date']
 trigger_dag(
            dag_id=self.trigger_dag_id,
            run_id=run_id,
            conf=self.conf,
            # own execution date passed to child DAG
            execution_date=self.execution_date,
            replace_microseconds=False,
        )