|
The existing SLA implementation before Airflow 3.0 has been problematic and a constant point of debate. In the Airflow 3 planning meetings, it was discussed and decided that the move to a new major version is a good time to tear out the old implementation and replace it with something new. Since AIP-57 has been abandoned, this proposal will replace and supersede AIP-57. That AIP and the discussion around it are not to be considered as part of this proposal and, unless mentioned here, nothing in those discussions or that AIP is considered to be part of this one.
The old SLA feature is removed in Airflow 3.0 (PR here) and in 3.1 it will be replaced with Deadline Alerts. The purpose of this AIP is to discuss the implementation of the new Deadline Alerts feature to replace the SLA feature.
One of the main points of contention of the SLA feature revolves around when you start counting. If you set an SLA of "1 hour", is that one hour after it was scheduled, one hour after it starts running, one hour after it was queued, or something else entirely. This leads to frequent questions and confusion. This AIP revolves around the idea of allowing the user to define that starting point and treat the new SLA as a calculated "need-by date". To avoid confusion, the new feature will be referred to as Deadlines or Deadline Alerts rather than SLA and the implementation will allow a much more versatile experience for the user.
(credit for this section to Sung Yun in AIP-57)
As stated above, this AIP intentionally ignores all previous discussion on the topic or fixing, changing, redefining, or re-implementing "SLA". Previous discussions have been ongoing for a very long time, across many different mediums, and have gone through multiple proposals. It is impossible to summarize or collate all those discussions, so treat this as a "blank slate" proposal and do not assume any other changes outside of what is explicitly stated herein.
As this is a topic that has been discussed many times, in an effort to avoid confusion, I am using the following definitions in this project.
An Enum will contain a selection of options, similar to how a user currently provides the TriggerRule. The implementation will include an interface which allows future work to easily add new reference timestamps, but they will not be a freeform input for the user. These options will resolve to a datetime() representing the possible points in time to start counting from. This value can be dynamic (examples include NEXT_DAGRUN_EXECUTION_DATE or DAGRUN_QUEUED_AT, which map to their respective database fields), or a static date/time value provided as a string and cast to a datetime() object.
A datetime.timedelta. When added to the reference timestamp you get the “need-by date”. (see below)
“Need-by” will be defined as the reference timestamp (see above) plus some delta. For example, `need_by = DeadlineAlerts.NEXT_DAGRUN_EXECUTION_DATE + 1_hour`
A Callable (or, by extension, a functools.partial) to execute when a miss is detected. This implementation will allow for either a custom callback, as seen in success_callback and failure_callback, and special attention will go into making sure using any existing or custom Notifiers is as seamless as possible.
Before diving into the challenges with the current SLA feature, let's consider what our users are expecting from an SLA feature in Airflow:
(Credit for user stories to Shubham Mehta )
Elmo, a data engineer at a large e-commerce company, manages a DAG that generates a critical daily sales report. This report must be ready by 9 AM every day for the executive team's business review.
Elmo expects:
Rosita, a data scientist at a finance firm, works with a DAG that includes a data processing task which transforms the data using EMR extracted from an external API. This task occasionally runs much longer than expected, delaying downstream processes.
Rosita needs:
Zoe, an engineer at a tech startup, manages a DAG with multiple stages: data ingestion, preparation, processing, and reporting. She needs to ensure each stage completes within a specific timeframe.
Zoe requires:
I may either go with the class init() pattern or the builder pattern, that will be decided at implementation, but usage would look something like one of these. I am currently leaning towards the format of Example 2.
# Example 1
with DAG(
dag_id="email_if_not_done_1_hour_after_dagrun_queued_example",
schedule=None,
start_date=yesterday,
tags=["deadline_alerts_example"],
deadline=Deadline(
reference=DeadlineReference.DAGRUN_QUEUED_AT,
interval=datetime.timedelta(hour=1),
callback=send_smtp_notification(
from_email="someone@mail.com",
to="someone@mail.com",
subject="[Error] The dag {{ dag.dag_id }} failed",
html_content="debug logs",
)
)
) as dag:
...
|
# Example 2
with DAG(
dag_id="custom_callback_if_not_done_1_hour_after_dagrun_scheduled_example",
schedule=daily,
start_date=yesterday,
tags=["deadline_alerts_example"],
deadline=DeadlineAlerts.DAGRUN_SCHEDULED_AT.build(
interval=datetime.timedelta(hour=1),
callback=functools.partial(my_custom_callback, arg1="Something went wrong!", arg2="Error Code: 123"),
)
) as dag:
...
|
# Example 3
with DAG(
dag_id="same_as_above_but_longer",
schedule=daily,
start_date=yesterday,
tags=["deadline_alerts_example"],
deadline=DeadlineAlerts.DAGRUN_SCHEDULED_AT
.with_interval(datetime.timedelta(days=1))
.callback(my_custom_callback)
.callback_kwargs({"arg1": "Something went wrong!", "arg2": "Error Code: 123"})
.build()
) as dag:
...
|
# Example 4
with DAG(
dag_id="email_if_not_run_in_over_24_hours_example",
schedule=daily,
start_date=yesterday,
tags=["deadline_alerts_example"],
deadline=DeadlineAlerts.DAG_LAST_EXECUTION_TIME.build(
interval=datetime.timedelta(hour=1),
callback=send_smtp_notification(
from_email="someone@mail.com",
to="someone@mail.com",
subject="[Error] The dag {{ dag.dag_id }} failed",
html_content="debug logs",
)
)
) as dag:
... |
New metrics will embrace an OTel-first design plans, meaning tags will be considered from the beginning, not an afterthought
Code will be added to the dagrun trigger so that manually-triggered dag will set “scheduled_start_time” to “utcnow()”
Changes will be made in such a way to minimize the conflict with the other potential Deadline types which are mentioned below under "Future Work"[Appendix 1].
Allowing the user to decide which point int he DAG's lifecycle the need-by time is based off will help clear up much of the confusion around the old SLA implementation.
All users who currently use the SLA feature will need to migrate. The SLA feature has been removed in 3.0 and replaced with a notification to this effect, and the documentation for the Deadline Alerts will include examples to set an Alert which works the same way as the old SLA to help minimize the transition effort for those users who liked the old way.
The SLAMiss table has been removed in 3.0 and this implementation will likely replace it with a similar DeadlineAlerts table which maps dagrun_id::timestamp.
DAGs which implement the old SLA feature will need to be manually migrated. Documentation will include examples showing the old vs new configurations to make that as easy as possible
This AIP will only cover DAG-level Deadlines.
Future work could add:
This is a list of all timestamps in the DB, included purely to inspire ideas of other ways which this feature might be used. Many of these are not practical to use as a basis for a Deadline, but user cases may come with with interesting ideas and these are included here for reference. The list is formatted as table_name - field_name