我有一个项目始于2018年,但我会定期为其添加一些新的DAG。因此,我的所有DAG在2018年都有start_date
,每天有schedule_interval
,但是catch_up
设置为False,因为当我现在添加新的DAG时,我不希望它运行自2018年以来的每一天(就目前而言,也许我将不得不在所有这些天一直运行它)。
但是,在大多数情况下,我希望它在添加日期之前运行几周。
我希望dag在start_date
和added_date
(我添加dag的日期)之间运行,在DAG Tree View UI中显示为白色圆圈,因此,我可以手动触发它。最后两个星期。
但是此视图中什么也没有出现...
因此,我手动运行了回填(从命令行... UI中的回填界面会很不错),但是回填执行的所有运行都不会出现在UI中。因此,如果一次回填失败,我仍然无法从用户界面重新运行。
是“未显示start_date
和added_date
之间的可能的dag运行。气流的预期行为?有什么办法可以克服这个问题?还是有更好的方法来处理此用例:“添加DAG,并在过去的某些日期手动运行它。”
根据Philipp的建议,一种解决方案是打开追赶,并将start_date
和add_date
之间的所有运行标记为成功(或失败,无论如何)。
我最终得到了这样的东西:
import datetime
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from airflow.utils.state import State
start_date = datetime.datetime(2020, 10, 1, tzinfo=datetime.timezone.utc)
add_date = datetime.datetime.now(datetime.timezone.utc)
# Define a DAG with just one task
with DAG("test_catchup", schedule_interval="@daily", catchup=True, start_date=start_date) as dag:
BashOperator(
task_id="print",
bash_command="echo {{ ds }}"
)
# For all dates between start_date and add_date, create a dagrun and mark it as failed.
for d in [start_date + datetime.timedelta(n) for n in range(int((add_date - start_date).days))]:
print("Create dagrun for ", d.isoformat())
try:
dag.create_dagrun(run_id="programmatic_fill_{}".format(d.isoformat()), state=State.FAILED, execution_date=d, start_date=add_date, external_trigger=False)
except Exception as e:
print("Error:", e)
pass
如您所见,首先,您必须将dagrun创建嵌套在try-except块中,因为Airflow每次读取此文件时,它将尝试在dagrun数据库中添加相同的条目,并因某些主键而失败冲突。
这大致可行。我所有的dagruns出现:
但是,我不能(重新)运行其中任何一个。 清除一个错误时,出现以下错误:
没有要清除的任务实例
我设法将一个标记为成功(将圆形和正方形变为绿色),然后清除它以将DAG(圆形)变为运行状态,但是将任务变为None
状态,然后它永远不会执行...
从Philipp的另一个好主意开始,我尝试了LatestOnlyOperator
。
import datetime
from airflow import DAG
from airflow.operators.bash_operator import BashOperator
from airflow.utils.state import State
from airflow.operators.latest_only_operator import LatestOnlyOperator
start_date = datetime.datetime(2020, 10, 1)
with DAG("test_latest_only", schedule_interval="@daily", catchup=True, start_date=start_date) as dag:
LatestOnlyOperator(
task_id="latest_filter"
) >> BashOperator(
task_id="print",
bash_command="echo {{ ds }}"
)
结果(我已经手动重新运行了第一个dagrun):
优点:
缺点:
最后,在使用catchup=False
选项之前,此运算符似乎是一个老技巧,并且由于已经讨论过弃用它,因此我不确定它的可持续性。
答案 0 :(得分:0)
我终于设法从“ Programmatic DAG run”的想法中得到启发,找到合适的解决方案。
该错误消息指出存在No task instances to clear
。
因此,解决方案是在创建DAG运行时创建任务实例。
这是我的工作解决方案:
import datetime
from airflow import DAG
from airflow.models import TaskInstance
from airflow.operators.bash_operator import BashOperator
from airflow.utils.state import State
from airflow.utils.timezone import parse
start_date = parse(datetime.datetime(2020, 11, 1).isoformat())
add_date = datetime.datetime(2020, 11, 8, tzinfo=datetime.timezone.utc)
# Create your dag
with DAG("test_task_instance", schedule_interval="@daily", catchup=False, start_date=start_date) as dag:
# And its tasks
BashOperator(
task_id="print",
bash_command="echo '{{ ti }}' > /tmp/{{ ds }}"
)
# For each expected execution date
for d in dag.date_range(dag.start_date, end_date=add_date)[:-1]:
# If no DAG run already exists
if dag.get_dagrun(d) is None:
# Create a new one (marking the state as SKIPPED)
dagrun = dag.create_dagrun(run_id="programmatic_fill_{}".format(d.isoformat()),
state=State.SKIPPED,
execution_date=d,
start_date=add_date,
external_trigger=False)
# And create task instance for each task for this DAG run
for t in dag.tasks:
TaskInstance(task=t, execution_date=dagrun.execution_date)
如您所见,在创建新的try...except
块之前,我们没有要求。
SKIPPED
状态似乎更适合于这些DAG运行,因为实际上没有任何运行。
然后,在气流UI中查看DAG时。
最后,您可以清除并运行调度程序来挑选要运行的dag。
编辑:
我编辑了下面的代码,该代码取代了调度程序,在调度程序创建它们之前创建了“赶上”的dags。
当前的解决方案(不是我认为最好的解决方案)是删除最后一个预期的执行日期。
- for d in dag.date_range(dag.start_date, end_date=add_date):
+ for d in dag.date_range(dag.start_date, end_date=add_date)[:-1]: