等待execution_date范围内的一组外部DAG

时间:2017-12-15 22:37:38

标签: airflow apache-airflow

我有一个每5分钟运行一次的DAG(让我们称之为5_min_dag)和另一个DAG,每天使用当前某些5_min_dag运行的输出运行(让我们调用{{ 1}})。

如何确保daily_dag等待当天daily_dag次运行的完成?

用于说明问题的一些简化代码:

5_min_dag
# ./5_min_dag.py
5_min_dag = DAG('5_min_dag', schedule_interval=timedelta(minutes=5))
5_min_task = BashOperator(
    task_id='5_min_task', 
    bash_command="echo date", 
    dag=5_min_dag
)

这可能吗?

也许使用ExternalTask​​Sensor和/或SubDagOperator?

4 个答案:

答案 0 :(得分:2)

我找到解决此问题的更好方法是使用SQLSensor查询气流元数据数据库。

首先,需要设置数据库的connection。我使用Web UI设置名为mysql_default的连接。

以下运算符被设置为daily_dag中的第一个任务。只有在5_min_dag daily_dag execution_date status==success wait_for_5_min_dags = SqlSensor( task_id='wait_for_all_5_min_dags', conn_id='mysql_default', sql=""" SELECT GREATEST(COUNT(state)-287, 0) FROM dag_run WHERE (execution_date BETWEEN '{{execution_date.replace(hour=0,minute=0)}}' AND '{{execution_date.replace(hour=23,minute=59)}}') AND dag_id='5_min_dag' AND state='success'; """ ) SQLSensor之后,它才会成功。

288
仅当查询返回非空或非零结果时,

24*60/5=288才会成功。所以这个查询被写为返回0,直到我们在当天找到23成功的dag运行(24)。如果我们等待每小时运行一次dag,我们会减去Traceback (most recent call last): File "test2.py", line 1, in <module> from ethernet_frame import * File "/home/bene/python/ethernet_frame.py", line 15, in <module> from ipv6_packet import Ipv6Packet File "/home/bene/python/ipv6_packet.py", line 17, in <module> from ipv4_packet import Ipv4Packet File "/home/bene/python/ipv4_packet.py", line 17, in <module> from ipv6_packet import Ipv6Packet ImportError: cannot import name 'Ipv6Packet ,因为我们每天都在等待insgesamt 76K drwxr-xr-x 3 bene bene 330 19. Jan 16:03 . drwx------ 24 bene bene 4,0K 19. Jan 16:06 .. -rw-r--r-- 1 bene bene 42 5. Jan 12:38 country.py -rw-r--r-- 1 bene bene 8,0K 5. Jan 12:09 dns_packet.py -rw-r--r-- 1 bene bene 1,6K 5. Jan 12:09 ethernet_frame.py -rw-r--r-- 1 bene bene 3,0K 5. Jan 12:09 icmp_packet.py -rw-r--r-- 1 bene bene 7,7K 5. Jan 12:09 ipv4_packet.py -rw-r--r-- 1 bene bene 2,7K 5. Jan 12:09 ipv6_packet.py -rw-r--r-- 1 bene bene 6,4K 5. Jan 12:09 microsoft_network_monitor_v2.py -rw-r--r-- 1 bene bene 7,0K 5. Jan 12:09 pcap.py drwxr-xr-x 2 bene bene 180 5. Jan 12:12 __pycache__ -rw-r--r-- 1 bene bene 1,1K 5. Jan 12:09 tcp_segment.py -rw-r--r-- 1 bene bene 518 5. Jan 12:32 test1.py -rw-r--r-- 1 bene bene 596 19. Jan 15:56 test2.py -rw-r--r-- 1 bene bene 667 5. Jan 12:38 test.py -rw-r--r-- 1 bene bene 880 5. Jan 12:09 udp_datagram.py -rw-r--r-- 1 bene bene 986 5. Jan 12:09 windows_systemtime.py dags。

答案 1 :(得分:1)

我遇到了类似的问题。所有每小时实例完成后,我尝试使用TriggerDagRunOperator。 对于所有每小时实例,气流变量都会更新。如果没有23次运行,它将触发另一个dag(每日dag,尽管其他dag现在将具有schedule_interval = None)。触发新dag时,需要再次将变量值设置为0。 (如果触发的dag失败,问题就在这里?)

dag = DAG('airflow_examples.hourly_to_daily_dependency_controller', description='DAG which prints Hello World',
              schedule_interval='0 * * * *',
              start_date=datetime(2018, 4, 15), catchup=True, default_args=default_args)

echo_task = BashOperator(
        task_id='echo_hello_world',
        bash_command='echo "in Hourly Dag: Hello"',
        dag=dag)

def update_variable(ts,**kwargs):
        hourly_dag_runs = Variable.get('hourly_dag_runs')
        hourly_dag_runs = int(hourly_dag_runs) + 1
        Variable.set('hourly_dag_runs', hourly_dag_runs)


update_hourly_dag_runs = PythonOperator(
        task_id='update_hourly_dag_runs',
        provide_context=True,
        python_callable=update_variable,
        dag=dag)

def conditionally_trigger(context, dag_run_obj):
    hourly_dag_runs = Variable.get('hourly_dag_runs')
    if (hourly_dag_runs == "23"):
        dag_run_obj.payload = {'message': hourly_dag_runs}
        Variable.set('hourly_dag_runs', 0)
        return dag_run_obj

trigger = TriggerDagRunOperator(task_id='test_trigger_dagrun',
                                    trigger_dag_id="airflow_examples.trigger_dailydag_run",
                                    python_callable=conditionally_trigger,
                                    dag=dag)

echo_task >> update_hourly_dag_runs >> trigger

答案 2 :(得分:0)

它并不漂亮但我通过生成一长串ExternalTask​​Sensors来完成这项工作:

for tdelta in range(0, 288):  # 288 5-minute dags per day (24*60/5=288)
    ExternalTaskSensor(
        task_id='wait_for_5_min_dag_'+str(tdelta),
        external_dag_id='5_min_dag',
        external_task_id='5_min_task',
        execution_delta=timedelta(minutes=tdelta),
        dag=daily_dag
    )

但这看起来不会很好,取决于5_min_dag中的任务,而不是dag本身。这意味着如果5_min_dag有分支,它将无效。

要使用分支机构,也可以允许skipped状态

for tdelta in range(0, 288):  
    ExternalTaskSensor(
        task_id='wait_for_5_min_dag_'+str(tdelta),
        external_dag_id='5_min_dag',
        external_task_id='5_min_task',
        allowed_states=['success','skipped'],  # skipped means this branch not followed
        execution_delta=timedelta(minutes=tdelta),
        dag=daily_dag
    )
return daily_dag

但是你需要为5_min_dag的每个分支设置一组这些传感器。

为了防止使用这些传感器填充GUI,可以将它们包装在子标签中。

这种方法的另一个问题是它产生了大量的等待&#34;任务一下子就很容易导致陷入僵局的状态。

让我们希望有人能找到更好的解决方案。

答案 3 :(得分:0)

您可以采取的一种方法是在5分钟的DAG中结合使用ShortCircuit运算符和TiggerDagRun运算符。

A(您的实际任务)-> B(短路电路操作员)-> C(TriggerDagRunOperator)

在ShortCircuitOperator中,您可以使用python函数检查执行日期,并且一天5分钟的一天的最后执行日期为2018-08-10 23:55:55。您可以使用python日期时间库来检查它是否这次,然后才可以执行ShortCircuitOperator。

现在,要检查当天的所有前5分钟运行是否都在ShortCicuitOperator的同一python函数中是否成功,您可以使用气流的DagRun类编写和运行,而不必建立与气流数据库的单独连接。所以看起来像

DagRun.find(dag_id="5_min_task",
            state=State.SUCCESS,
            execution_date=[all 5 min execution dates])

您可以运行循环以检查上述函数返回的计数,并在不匹配的情况下进入睡眠状态,并继续检查直到所需的dag数匹配为止。