簡體   English   中英

當外部任務失敗時,氣流外部任務傳感器不會失敗

[英]Airflow ExternalTaskSensor don't fail when External Task fails

我試圖在 Airflow 1.10.11 中使用ExternalTaskSensor來管理一些 dag 的坐標。 我開發了這段代碼來測試功能:

import time
from datetime import datetime, timedelta
from pprint import pprint

from airflow import DAG
from airflow.operators.dagrun_operator import TriggerDagRunOperator
from airflow.operators.dummy_operator import DummyOperator
from airflow.operators.python_operator import PythonOperator
from airflow.sensors.external_task_sensor import ExternalTaskSensor
from airflow.utils.state import State

sensors_dag = DAG(
    "test_launch_sensors",
    schedule_interval=None,
    start_date=datetime(2020, 2, 14, 0, 0, 0),
    dagrun_timeout=timedelta(minutes=150),
    tags=["DEMO"],
)

dummy_dag = DAG(
    "test_dummy_dag",
    schedule_interval=None,
    start_date=datetime(2020, 2, 14, 0, 0, 0),
    dagrun_timeout=timedelta(minutes=150),
    tags=["DEMO"],
)


def print_context(ds, **context):
    pprint(context['conf'])


with dummy_dag:
    starts = DummyOperator(task_id="starts", dag=dummy_dag)
    empty = PythonOperator(
        task_id="empty",
        provide_context=True,
        python_callable=print_context,
        dag=dummy_dag,
    )
    ends = DummyOperator(task_id="ends", dag=dummy_dag)

    starts >> empty >> ends

with sensors_dag:
    trigger = TriggerDagRunOperator(
        task_id=f"trigger_{dummy_dag.dag_id}",
        trigger_dag_id=dummy_dag.dag_id,
        conf={"key": "value"},
        execution_date="{{ execution_date }}",
    )
    sensor = ExternalTaskSensor(
        task_id="wait_for_dag",
        external_dag_id=dummy_dag.dag_id,
        external_task_id="ends",
        failed_states=["failed", "upstream_failed"],
        poke_interval=5,
        timeout=120,
    )
    trigger >> sensor

這個想法是一個 dag 用TriggerDagRunOperator觸發另一個。 這會將兩個 dag 中的execution_date設置為相同的值。 這完美的作品時的狀態dummy_dag最后一個任務, ends ,是success

但是,如果我強制中間任務像這樣失敗:

def print_context(ds, **context):
    pprint(context['conf'])
    raise Exception('ouch')

Sensor 不會檢測到failedupstream_failed狀態,它會一直運行直到超時。 我使用failed_states參數來指示需要將哪些狀態視為失敗,但似乎不起作用。

難道我做錯了什么?

failed_states是在 Airflow 2.0 中添加的; 如果受監控的 DAG 運行失敗,您可以將其設置為["failed"]以將傳感器配置為使當前 DAG 運行失敗。 如果給定任務 ID,它將監視任務狀態,否則監視 DAG 運行狀態。

不幸的是,在 Airflow 1.x 中, ExternalTaskSensor操作僅將 DAG 運行或任務狀態與allowed_states進行比較; 一旦受監控的 DAG 運行或任務達到允許的狀態之一,傳感器就會停止,然后始終標記為成功。 默認情況下,傳感器只查找SUCCESS狀態,因此如果受監控的 DAG 運行失敗,它會在沒有超時的情況下永遠繼續探查。 如果您將failed放入allowed_states列表中,它仍然只會將自己標記為成功。

雖然你可以使用超時,像你我所需要的傳感器失效它自己的運行DAG如果外部DAG運行失敗,仿佛下一個任務的依賴性還沒有得到滿足。 不幸的是,這需要您編寫自己的傳感器。

這是我的實現; 它是ExternalTaskSensor()類的簡化版本,適合我更簡單的需求(無需檢查特定任務 ID 或除相同執行日期之外的任何其他內容):

from airflow.exceptions import AirflowFailException
from airflow.models import DagRun
from airflow.sensors.base_sensor_operator import BaseSensorOperator
from airflow.utils.db import provide_session
from airflow.utils.decorators import apply_defaults
from airflow.utils.state import State

class ExternalDagrunSensor(BaseSensorOperator):
    """
    Waits for a different DAG to complete; if the dagrun has failed, this
    task fails itself as well.

    :param external_dag_id: The dag_id that contains the task you want to
        wait for
    :type external_dag_id: str
    """

    template_fields = ["external_dag_id"]
    ui_color = "#19647e"

    @apply_defaults
    def __init__(self, external_dag_id, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.external_dag_id = external_dag_id

    @provide_session
    def poke(self, context, session=None):
        dag_id, execution_date = self.external_dag_id, context["execution_date"]
        self.log.info("Poking for %s on %s ... ", dag_id, execution_date)

        state = (
            session.query(DagRun.state)
            .filter(
                DagRun.dag_id == dag_id,
                DagRun.execution_date == execution_date,
                DagRun.state.in_((State.SUCCESS, State.FAILED)),
            )
            .scalar()
        )
        if state == State.FAILED:
            raise AirflowFailException(
                f"The external DAG run {dag_id} {execution_date} has failed"
            )
        return state is not None

基本傳感器實現將重復調用poke()方法,直到它返回True (或達到可選超時),並通過引發AirflowFailException將任務狀態設置為立即失敗,不再重試。 如果它們將被安排運行,則由下游任務配置決定。

ExternalTask​​Sensor 只是戳直到達到某個預期狀態,它的狀態不打算與外部任務狀態映射。

它默認為 [State.SUCCESS] 這就是為什么如果成功你沒有任何問題。 將 allowed_states=[State.SUCCESS, State.failed, State.upstream_failed] 添加到您的代碼中至少可以確保外部任務已完成。

此外,您可以設置超時以使其失敗,如果 soft_fail = False

如果您希望傳感器在外部任務失敗時失敗,您需要編寫自己的此類傳感器實現。

例如,這里是我如何檢查 Dag 的 Last Dagrun 以匹配特定狀態

@provide_session
def poke(self, context, session=None):
    """
    Checks if latest dag_run State is in expected state else keeps polling...
    :param context:
    :param session:
    :return:
    """
    DR = DagRun
    self.log.info(
        f"Poking for {self.external_dag_id}, {self.allowed_states} -> {self.state_condition} ... "
    )
    # If state is expected to match
    if self.state_condition:
        query = session.query(DR).filter(DR.dag_id == self.external_dag_id,
                                                   DR.state.notin_(self.allowed_states))
    # If state is not expected to match
    else:
        query = session.query(DR).filter(DR.dag_id == self.external_dag_id,
                                                   DR.state.in_(self.allowed_states))
    # Filter by last_dagrun, could be max(execution_date) also but avoiding such aggregation
    # by sorting dag_run chronologically in descendent order
    query = query.order_by(DR.execution_date.desc()).first()
    session.commit()
    return not query

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM