在 Apache 气流中实现跨 DAG 依赖

时间:2021-05-29 11:06:12

标签: airflow directed-acyclic-graphs

我正在尝试在 2 个 DAG 之间实现 DAG 依赖,例如 A 和 B。DAG A 每小时运行一次,DAG B 每 15 分钟运行一次。

  1. 每次 DAG B 启动时都会运行,我想确保 DAG A 未处于运行状态。
  2. 如果发现 DAG A 正在运行,则 DAG B 必须等到 DAG A 完成运行。
  3. 如果 DAG A 未运行,DAG B 可以继续执行其任务。

DAG :

from datetime import datetime,timedelta
from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator

default_args = {
    'owner': 'dependency',
    'depends_on_past': False,
    'start_date': datetime(2020, 9, 10, 10, 1),
    'email': ['xxxx.com'],
    'email_on_failure': True,
    'email_on_retry': False,
    'retries': 1,
    'retry_delay': timedelta(minutes=5)
}

with DAG('DAG_A', schedule_interval='0/60 * * * *',max_active_runs=1, catchup=False,
         default_args=default_args) as dag:

    task1 = DummyOperator(task_id='task1', retries=1, dag=dag)
    task2 = DummyOperator(task_id='task2', retries=1, dag=dag)
    task3 = DummyOperator(task_id='task3', retries=1, dag=dag)

    task1 >> task2 >> task3

DAG B:

from datetime import datetime,timedelta
from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator

default_args = {
    'owner': 'dependency',
    'depends_on_past': False,
    'start_date': datetime(2020, 9, 10, 10, 1),
    'email': ['xxxx.com'],
    'email_on_failure': True,
    'email_on_retry': False,
    'retries': 1,
    'retry_delay': timedelta(minutes=5)
}

with DAG('DAG_B', schedule_interval='0/15 * * * *',max_active_runs=1, catchup=False,
        default_args=default_args) as dag:

    task4 = DummyOperator(task_id='task4', retries=1, dag=dag)
    task5 = DummyOperator(task_id='task5', retries=1, dag=dag)
    task6 = DummyOperator(task_id='task6', retries=1, dag=dag)

    task4 >> task5 >> task6

我尝试过使用 ExternalTask​​Sensor 操作符。我无法理解传感器是否发现 DAG A 处于成功状态,它会触发下一个任务,否则等待任务完成。

提前致谢。

2 个答案:

答案 0 :(得分:1)

我认为以“一般”方式实现这一目标的唯一方法是使用一些外部锁定机制

虽然使用池,您可以获得相当好的近似值:

https://airflow.apache.org/docs/apache-airflow/1.10.3/concepts.html?highlight=pool

如果您将池大小设置为 1 并将 dag A 和 B 都分配给池,则一次只能运行其中之一。您还可以以您认为最合适的方式添加 priority_weight - 以防您需要将 A 优先于 B 或相反。

答案 1 :(得分:1)

您可以使用 ExternalTaskSensor 来实现您的目标。关键是用正确的 execution_date 初始化这个传感器,在你的例子中是 DAG_A 的最后一个 execution_dateDagRun。 检查此示例,其中 DAG_A 每 9 分钟运行一次,持续 200 秒。 DAG_B 每 3 分钟运行一次,持续 30 秒。这些值是任意的,仅用于演示目的,几乎可以是任何值。

DAG A(这里没有什么新东西):

import time
from airflow import DAG
from airflow.models.baseoperator import chain
from airflow.operators.dummy import DummyOperator
from airflow.operators.python import PythonOperator
from airflow.utils.dates import days_ago


def _executing_task(**kwargs):
    print("Starting task_a")
    time.sleep(200)
    print("Completed task_a")


dag = DAG(
    dag_id="example_external_task_sensor_a",
    default_args={"owner": "airflow"},
    start_date=days_ago(1),
    schedule_interval="*/9 * * * *",
    tags=['example_dags'],
    catchup=False
)
with dag:

    start = DummyOperator(
        task_id='start')

    task_a = PythonOperator(
        task_id='task_a',
        python_callable=_executing_task,
    )

chain(start, task_a)

DAG B:

import time
from airflow import DAG
from airflow.utils.db import provide_session
from airflow.models.dag import get_last_dagrun
from airflow.models.baseoperator import chain
from airflow.operators.dummy import DummyOperator
from airflow.operators.python import PythonOperator
from airflow.utils.dates import days_ago
from airflow.sensors.external_task import ExternalTaskSensor


def _executing_task():
    time.sleep(30)
    print("Completed task_b")


@provide_session
def _get_execution_date_of_dag_a(exec_date, session=None,  **kwargs):
    dag_a_last_run = get_last_dagrun(
        'example_external_task_sensor_a', session)
    print(dag_a_last_run)
    print(f"EXEC DATE: {dag_a_last_run.execution_date}")
    return dag_a_last_run.execution_date


dag = DAG(
    dag_id="example_external_task_sensor_b",
    default_args={"owner": "airflow"},
    start_date=days_ago(1),
    schedule_interval="*/3 * * * *",
    tags=['example_dags'],
    catchup=False
)
with dag:

    start = DummyOperator(
        task_id='start')

    wait_for_dag_a = ExternalTaskSensor(
        task_id='wait_for_dag_a',
        external_dag_id='example_external_task_sensor_a',
        allowed_states=['success', 'failed'],
        execution_date_fn=_get_execution_date_of_dag_a,
        poke_interval=30
    )
    task_b = PythonOperator(
        task_id='task_b',
        python_callable=_executing_task,
    )

chain(start, wait_for_dag_a,  task_b)

我们正在使用 execution_date_fn 的参数 ExternalTaskSensor 来获取 DAG_A 的最后一个 DagRun 的 execution_date,如果我们不这样做这样做,它将等待 DAG_ADAG_B 的实际运行相同的 execution_date,这在许多情况下可能不存在。

函数 _get_execution_date_of_dag_a 使用来自 Airflow 模型的 get_last_dagrun 查询元数据数据库以获取 exec_date。

最后,另一个重要参数是 allowed_states=['success', 'failed'],我们告诉它等待 DAG_A 处于这些状态之一(即,如果它处于 running 状态将继续执行poke)。

试试看,告诉我它是否适合你!