Python 动态更改任务重试次数

Python 动态更改任务重试次数,python,airflow,Python,Airflow,重试一项任务可能毫无意义。例如,如果任务是一个传感器,并且由于其凭据无效而失败,那么将来的任何重试都将不可避免地失败。如何定义可以决定重试是否合理的运算符 在Airflow 1.10.6中,决定任务是否应重试的逻辑位于Airflow.models.taskinstance.taskinstance.handle\u failure中,因此无法在操作员中定义行为,因为这是任务的责任,而不是操作员的责任 理想的情况是,如果在操作员端定义了处理故障方法,那么我们可以根据需要重新定义它 我找到的唯一解决

重试一项任务可能毫无意义。例如,如果任务是一个传感器,并且由于其凭据无效而失败,那么将来的任何重试都将不可避免地失败。如何定义可以决定重试是否合理的运算符

在Airflow 1.10.6中,决定任务是否应重试的逻辑位于
Airflow.models.taskinstance.taskinstance.handle\u failure
中,因此无法在操作员中定义行为,因为这是任务的责任,而不是操作员的责任

理想的情况是,如果在操作员端定义了
处理故障
方法,那么我们可以根据需要重新定义它

我找到的唯一解决方法是使用
PythonBranchingOperator
来“测试”任务是否可以运行。例如,对于上面的传感器,检查登录凭据是否有效,然后将DAG流带到传感器。否则,将失败(或转移到另一个任务)


我对
handle\u故障的分析是否正确?有更好的解决方法吗?

您可以从上下文中获取相应的任务实例,并重新定义重试次数,例如:

从datetime导入datetime,timedelta
从气流导入DAG
从afflow.operators.python_operator导入PythonOperator
默认参数={
“所有者”:“气流”,
“开始日期”:日期时间(2011,1,1,1,1),
}
def fun(*,任务_实例,**上下文):
task_instance.max_trys=0#将重试次数重置为0
引发异常()
将DAG(“my_DAG”,default_args=default_args,catchup=False)作为DAG:
op=蟒蛇算子(
task_id=“my_op”,
python_callable=fun,
提供上下文=True,
重试次数=100000次,#设置大量重试次数
重试延迟=时间增量(秒=1),
)

我个人不会动态地重新定义重试次数,因为它会以一种不明显的方式在操作员内部改变工作流行为,从而使工作流的推理复杂化。我只会让任务失败设置次数,与失败原因无关。如果退休人员的费用很高,我会减少他们的人数(例如,减少到1或0)。

回答我自己的问题,通过修改所有运算符中可用的
self.retries
实例变量,在
execute
方法中,我们可以动态强制不再重试

在以下示例中:

  • 传感器0:第一次尝试将成功
  • 传感器1:尝试4次后将失败(最多1+3次重试)
  • 传感器2:尝试1次后将失败(强制不再动态重试)
  • 甘特图显示每个任务的尝试次数

    Meta:我不确定我问题的标题。如果有人想编辑它以使其更清晰,请随意。我建议使用传感器(而不是干预假定不可变的
    重试次数
    属性),我不知道该异常,但据我所知,它将跳过任务而不会失败。是否可用于失败?抛出任何异常(除了
    AirflowSkipException
    )将导致任务失败是,但
    AirflowSkipException
    跳过将来的重试。任何其他异常都将导致将来重试(这是这个StackOverflow问题的基础)。我不知道如何从操作员处访问task_实例。如果错误不可恢复,那么定义一个新操作符并将self.retries设置为0怎么样?它不需要修改实例。方法
    aiffort.models.taskinstance.taskinstance.is_qualified_重试
    使用
    self.task.retries
    因此,如果任务的
    retries
    实例变量在运行时被修改,则其行为将受到影响。只是尝试了一下,在新操作符的
    execute
    方法中执行
    self.retries=0
    ,以后不会强制重试。我们不会像您在评论中那样被迫修改
    task\u实例
    对象。
    from datetime import datetime, timedelta
    
    from airflow import DAG
    from airflow.models import BaseOperator
    
    
    class PseudoSensor(BaseOperator):
        def __init__(
                self,
                s3_status_code_mock,
                *args,
                **kwargs):
            super().__init__(*args, **kwargs)
            self.s3_status_code_mock = s3_status_code_mock
    
        def execute(self, context):
            # Try to read S3, Redshift, blah blah
            pass
            # The query returned a status code, that we mock when the Sensor is initialized
            if self.s3_status_code_mock == 0:
                # Success
                return 0
            elif self.s3_status_code_mock == 1:
                # Error but should retry if I can still can
                raise Exception("Retryable error. Won't change retries of operator.")
            elif self.s3_status_code_mock == 2:
                # Unrecoverable error. Should fail without future retries.
                self.retries = 0
                raise Exception("Unrecoverable error. Will set retries to 0.")
    
    
    # A separate function so we don't make the globals dirty
    def createDAG():
        # Default (but overridable) arguments for Operators instantiations
        default_args = {
            'owner': 'Supay',
            'depends_on_past': False,
            'start_date': datetime(2019, 11, 28),
            'retry_delay': timedelta(seconds=1),
            'retries': 3,
        }
    
        with DAG("dynamic_retries_dag", default_args=default_args, schedule_interval=timedelta(days=1), catchup=False) as dag :
            # Sensor 0: should succeed in first try
            sensor_0 = PseudoSensor(
                task_id="sensor_0",
                provide_context=True,
                s3_status_code_mock=0,
            )
    
            # Sensor 1: should fail after 3 tries
            sensor_1 = PseudoSensor(
                task_id="sensor_1",
                provide_context=True,
                s3_status_code_mock=1
            )
    
            # Sensor 1: should fail after 1 try
            sensor_2 = PseudoSensor(
                task_id="sensor_2",
                provide_context=True,
                s3_status_code_mock=2
            )
    
            dag >> sensor_0
            dag >> sensor_1
            dag >> sensor_2
    
            globals()[dag.dag_id] = dag
    
    
    # Run everything
    createDAG()