我有一个每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
)
这可能吗?
也许使用ExternalTaskSensor和/或SubDagOperator?
答案 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)
它并不漂亮但我通过生成一长串ExternalTaskSensors来完成这项工作:
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数匹配为止。