You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by "ferruzzi (via GitHub)" <gi...@apache.org> on 2026/02/10 04:44:25 UTC
[PR] Fix deadline alert hashing bug [airflow]
ferruzzi opened a new pull request, #61702:
URL: https://github.com/apache/airflow/pull/61702
When I implemented UUID reuse to reduce DAG re-serialization, my strategy was too overzealous. I blindly reused UUIDs if the DAG previously had ANY deadlines, which caused two bugs:
1. If the Dag had multiple DeadlineAlerts, they failed silently (no new DeadlineAlert records were created)
2. Deadline property changes (interval/reference/callback) were ignored if you edited the Dag once it was initially parsed
We now compare existing DeadlineAlert records and check if any definitions changed.
**New logic:**
- Reuse UUIDs when ALL deadlines match (preserves hash, avoids unnecessary writes and re-serialization)
- Generate new UUIDs when any deadline changes (updates the hash and creates new records)
Also improved the relevant unit tests which would have caught this to prevent regression in the future.
<!--
Thank you for contributing!
Please provide above a brief description of the changes made in this pull request.
Write a good git commit message following this guide: http://chris.beams.io/posts/git-commit/
Please make sure that your code changes are covered with tests.
And in case of new features or big changes remember to adjust the documentation.
Feel free to ping (in general) for the review if you do not see reaction for a few days
(72 Hours is the minimum reaction time you can expect from volunteers) - we sometimes miss notifications.
In case of an existing issue, reference it using one of the following:
* closes: #ISSUE
* related: #ISSUE
-->
---
##### Was generative AI tooling used to co-author this PR?
<!--
If generative AI tooling has been used in the process of authoring this PR, please
change below checkbox to `[X]` followed by the name of the tool, uncomment the "Generated-by".
-->
- [ ] Yes (please specify the tool below)
<!--
Generated-by: [Tool Name] following [the guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
-->
---
* Read the **[Pull Request Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)** for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
* For fundamental code changes, an Airflow Improvement Proposal ([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals)) is needed.
* When adding dependency, check compliance with the [ASF 3rd Party License Policy](https://www.apache.org/legal/resolved.html#category-x).
* For significant user-facing changes create newsfragment: `{pr_number}.significant.rst` or `{issue_number}.significant.rst`, in [airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "Lee-W (via GitHub)" <gi...@apache.org>.
Lee-W commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2790986234
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
Review Comment:
non-blocking. I just point out when I noticed :)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "vincbeck (via GitHub)" <gi...@apache.org>.
vincbeck commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2824364594
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
+ :param new_deadline_data: List of new deadline alert data dicts from the DAG
+ :param session: Database session
+ :return: UUID mapping dict if all match, None if any mismatch detected
+ """
+ if len(existing_deadline_uuids) != len(new_deadline_data):
+ return None
+
+ existing_alerts = session.scalars(
+ select(DeadlineAlertModel).where(DeadlineAlertModel.id.in_(existing_deadline_uuids))
+ ).all()
+
+ if len(existing_alerts) != len(existing_deadline_uuids):
+ return None
+
+ new_alerts_temp = []
+ for deadline_alert in new_deadline_data:
+ deadline_data = deadline_alert.get(Encoding.VAR, deadline_alert)
+ # Create a temporary alert for comparison
+ temp_alert = DeadlineAlertModel(
+ id="temp", # id is required for the object but isn't included in the __eq__
Review Comment:
I understand the confusion but I think, indeed, using anew function is better. Why? Because if you have 2 `DeadlineAlertModel` identical objects but `id` and/or `serialized_dag_id` and you do a `==` comparison between these 2 object, Python will say they are identical, which is wrong, they are not
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "Lee-W (via GitHub)" <gi...@apache.org>.
Lee-W commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2786064832
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
+ :param new_deadline_data: List of new deadline alert data dicts from the DAG
Review Comment:
```suggestion
:param new_deadline_data: List of new deadline alert data dicts from the Dag
```
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
+ :param new_deadline_data: List of new deadline alert data dicts from the DAG
+ :param session: Database session
+ :return: UUID mapping dict if all match, None if any mismatch detected
+ """
+ if len(existing_deadline_uuids) != len(new_deadline_data):
+ return None
+
+ existing_alerts = session.scalars(
+ select(DeadlineAlertModel).where(DeadlineAlertModel.id.in_(existing_deadline_uuids))
+ ).all()
+
+ if len(existing_alerts) != len(existing_deadline_uuids):
+ return None
+
+ new_alerts_temp = []
+ for deadline_alert in new_deadline_data:
+ deadline_data = deadline_alert.get(Encoding.VAR, deadline_alert)
+ # Create a temporary alert for comparison
+ temp_alert = DeadlineAlertModel(
+ id="temp", # id is required for the object but isn't included in the __eq__
Review Comment:
Is it possible to have multiple DeadlineAlertModels with the same property but different IDs?
DeadlineAlertModel(id=1, reference="the same"...)
DeadlineAlertModel(id=2, reference="the same"...)
If it's possible, it looks incorrect to use `__eq__` to me. Comparing those attributes directly might seem lengthy, but it is probably more accurate and less confusing.
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -503,9 +565,20 @@ def write_dag(
and existing_serialized_dag.data
and (existing_deadline_uuids := existing_serialized_dag.data.get("dag", {}).get("deadline"))
):
- dag.data["dag"]["deadline"] = existing_deadline_uuids
- deadline_uuid_mapping = {}
+ deadline_uuid_mapping = cls._try_reuse_deadline_uuids(
+ existing_deadline_uuids,
+ dag.data["dag"]["deadline"],
+ session,
+ )
+
+ if deadline_uuid_mapping is not None:
+ # All deadlines matched, reuse the UUIDs to preserve hash.
+ dag.data["dag"]["deadline"] = list(deadline_uuid_mapping.keys())
+ else:
+ # At least one deadline has changed, generate new UUIDs and update the hash.
+ deadline_uuid_mapping = cls._generate_deadline_uuids(dag.data)
else:
+ # First time seeing this DAG with deadlines, generate new UUIDs and update the hash.
Review Comment:
```suggestion
# First time seeing this Dag with deadlines, generate new UUIDs and update the hash.
```
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
Review Comment:
```suggestion
:param existing_deadline_uuids: List of UUID strings from existing serialized Dag
```
##########
airflow-core/tests/unit/models/test_dag.py:
##########
@@ -1872,7 +1873,7 @@ def test_dagrun_deadline(self, reference_type, reference_column, testing_dag_bun
assert dr.deadlines[0].deadline_time == getattr(dr, reference_column, DEFAULT_DATE) + interval
def test_dag_with_multiple_deadlines(self, testing_dag_bundle, session):
- """Test that a DAG with multiple deadlines stores all deadlines in the database."""
+ """Test that a DAG with multiple deadlines stores all deadlines and persists on re-serialization."""
Review Comment:
```suggestion
"""Test that a Dag with multiple deadlines stores all deadlines and persists on re-serialization."""
```
##########
airflow-core/tests/unit/models/test_dag.py:
##########
@@ -1899,6 +1901,28 @@ def test_dag_with_multiple_deadlines(self, testing_dag_bundle, session):
scheduler_dag = sync_dag_to_db(dag, session=session)
+ deadline_alerts = session.scalars(select(DeadlineAlertModel)).all()
+ assert len(deadline_alerts) == expected_num_deadlines
+ initial_uuids = {alert.id for alert in deadline_alerts}
+
+ # Re-serialize the DAG
Review Comment:
```suggestion
# Re-serialize the Dag
```
##########
airflow-core/tests/unit/models/test_serialized_dag.py:
##########
@@ -753,3 +763,48 @@ def test_write_dag_atomicity_on_dagcode_failure(self, dag_maker, session):
assert len(sdag.dag.task_dict) == 1, (
"SerializedDagModel should not be updated when write fails"
)
+
+ def test_deadline_interval_change_triggers_new_serdag(self, testing_dag_bundle, session):
+ dag_id = "test_interval_change"
+
+ # Create a new Dag with a deadline and create a dagrun as a baseline..
Review Comment:
```suggestion
# Create a new Dag with a deadline and create a dagrun as a baseline.
```
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -503,9 +565,20 @@ def write_dag(
and existing_serialized_dag.data
and (existing_deadline_uuids := existing_serialized_dag.data.get("dag", {}).get("deadline"))
):
- dag.data["dag"]["deadline"] = existing_deadline_uuids
- deadline_uuid_mapping = {}
+ deadline_uuid_mapping = cls._try_reuse_deadline_uuids(
+ existing_deadline_uuids,
+ dag.data["dag"]["deadline"],
+ session,
+ )
+
+ if deadline_uuid_mapping is not None:
+ # All deadlines matched, reuse the UUIDs to preserve hash.
+ dag.data["dag"]["deadline"] = list(deadline_uuid_mapping.keys())
Review Comment:
if all matched, why don't we just use `existing_deadline_uuids`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "uranusjr (via GitHub)" <gi...@apache.org>.
uranusjr commented on PR #61702:
URL: https://github.com/apache/airflow/pull/61702#issuecomment-3875450503
This seems right but I kind of wonder if there’s a more straightforward design to this. Maybe by not using a UUID field on the model?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "ferruzzi (via GitHub)" <gi...@apache.org>.
ferruzzi commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2789665919
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -503,9 +565,20 @@ def write_dag(
and existing_serialized_dag.data
and (existing_deadline_uuids := existing_serialized_dag.data.get("dag", {}).get("deadline"))
):
- dag.data["dag"]["deadline"] = existing_deadline_uuids
- deadline_uuid_mapping = {}
+ deadline_uuid_mapping = cls._try_reuse_deadline_uuids(
+ existing_deadline_uuids,
+ dag.data["dag"]["deadline"],
+ session,
+ )
+
+ if deadline_uuid_mapping is not None:
+ # All deadlines matched, reuse the UUIDs to preserve hash.
+ dag.data["dag"]["deadline"] = list(deadline_uuid_mapping.keys())
Review Comment:
That's a really good catch, I was thinking I wanted to preserve the order from the helper but it really doesn't matter, does it... I think you are right; `existing_deadline_uuids` would be cleaner and easier to understand.
Let me think on it over lunch and see if I can think of any issues that would cause, but I'll likely implement that change.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "ferruzzi (via GitHub)" <gi...@apache.org>.
ferruzzi commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2790994071
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
Review Comment:
Nah, it was a fair callout and I've updated the ones I added. I'll make a note to get the others in this file later, but in a different PR. I promised to have this ready for 3.2 and time is tight
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "ferruzzi (via GitHub)" <gi...@apache.org>.
ferruzzi commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2819703767
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
+ :param new_deadline_data: List of new deadline alert data dicts from the DAG
+ :param session: Database session
+ :return: UUID mapping dict if all match, None if any mismatch detected
+ """
+ if len(existing_deadline_uuids) != len(new_deadline_data):
+ return None
+
+ existing_alerts = session.scalars(
+ select(DeadlineAlertModel).where(DeadlineAlertModel.id.in_(existing_deadline_uuids))
+ ).all()
+
+ if len(existing_alerts) != len(existing_deadline_uuids):
+ return None
+
+ new_alerts_temp = []
+ for deadline_alert in new_deadline_data:
+ deadline_data = deadline_alert.get(Encoding.VAR, deadline_alert)
+ # Create a temporary alert for comparison
+ temp_alert = DeadlineAlertModel(
+ id="temp", # id is required for the object but isn't included in the __eq__
Review Comment:
I don't particularly agree, I feel like that is exacatly what the `__eq__` method is meant for, but I've made the change. I replaced the custom `__eq__` and `hash` with `matches_definition`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "ferruzzi (via GitHub)" <gi...@apache.org>.
ferruzzi commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2789600592
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
+ :param new_deadline_data: List of new deadline alert data dicts from the DAG
+ :param session: Database session
+ :return: UUID mapping dict if all match, None if any mismatch detected
+ """
+ if len(existing_deadline_uuids) != len(new_deadline_data):
+ return None
+
+ existing_alerts = session.scalars(
+ select(DeadlineAlertModel).where(DeadlineAlertModel.id.in_(existing_deadline_uuids))
+ ).all()
+
+ if len(existing_alerts) != len(existing_deadline_uuids):
+ return None
+
+ new_alerts_temp = []
+ for deadline_alert in new_deadline_data:
+ deadline_data = deadline_alert.get(Encoding.VAR, deadline_alert)
+ # Create a temporary alert for comparison
+ temp_alert = DeadlineAlertModel(
+ id="temp", # id is required for the object but isn't included in the __eq__
Review Comment:
Yes, that can happen. Two different Dags can reuse the same DeadlineAlert object, for example:
```
my_deadline = DeadlineAlert(stuff)
with DAG(id="dag1", deadline=my_deadline,...) as dag1:
....
with DAG(id="dag2", deadline=my_deadline,...) as dag2:
....
```
would result in two DeadlineAlertModel entries identical other than their unique `id` and `serialized_dag_id` fields. I think the custom `__eq__` method is the right call here. The alternative to just compare those three fields between the two objects would just be duplicating that function, unless I'm missing something that you are seeing?
Or perhaps I misunderstood and the point was that the custom `__eq__` was what is causing confusion because someone might assume it's using object equivalence instead of just checking those three fields? Maybe I need a comment in the code to call out that it's a custom `__eq__` and not the default?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "Dev-iL (via GitHub)" <gi...@apache.org>.
Dev-iL commented on PR #61702:
URL: https://github.com/apache/airflow/pull/61702#issuecomment-3932241676
Here's what Opus had to say:
----
## PR Review: Deadline UUID Reuse on Re-serialization
**PR Goal:** When a DAG is re-serialized (e.g., during periodic parsing), preserve existing deadline alert UUIDs if the deadline definitions haven't changed, avoiding unnecessary `SerializedDagModel` hash changes and record churn.
---
### AIP Alignment
| AIP Requirement | Status | Notes |
| --- | --- | --- |
| DAG-level deadlines only | OK | Changes are scoped to DAG-level `DeadlineAlert` |
| Must not cause extra DAG file parsing | OK | Prevents unnecessary SerializedDagModel recreations caused by UUID regeneration |
| Extensible for future task/dataset deadlines | OK | `matches_definition` is a general-purpose comparison, no coupling to DAG-level specifics |
| Callbacks work with Notifier system | N/A | Not touched by this PR |
| OTel metrics from day one | N/A | Not touched by this PR |
---
### Issues
#### 1. `NotImplemented` from a non-dunder method — Medium
**File:** `deadline_alert.py:76-78`
```python
def matches_definition(self, other: DeadlineAlert) -> bool:
if not isinstance(other, DeadlineAlert):
return NotImplemented
```
`NotImplemented` is a truthy singleton intended for dunder comparison methods (`__eq__`, `__lt__`, etc.), where the Python runtime intercepts it to try the reflected operation. For a regular method:
- The return type annotation `-> bool` is a lie — callers using type checkers get no warning.
- `if alert.matches_definition(non_alert):` silently evaluates to `True`.
- In `_try_reuse_deadline_uuids`, the only call site passes two `DeadlineAlertModel` instances so this path isn't hit today, but it's a latent trap.
**Suggestion:** Either `raise TypeError(...)` or simply `return False`. If there's a reason to distinguish "not comparable" from "not equal", use `Optional[bool]` and return `None`.
---
#### 2. Temporary ORM object with `id="temp"` — Medium
**File:** `serialized_dag.py:449-454`
```python
temp_alert = DeadlineAlertModel(
id="temp",
reference=deadline_data[DeadlineAlertFields.REFERENCE],
interval=deadline_data[DeadlineAlertFields.INTERVAL],
callback_def=deadline_data[DeadlineAlertFields.CALLBACK],
)
```
This constructs a SQLAlchemy model instance with a bogus primary key solely to call `matches_definition`. Problems:
- If the `id` column gains UUID validation (or a DB-side default trigger), this breaks.
- The object may be inadvertently added to the session by SQLAlchemy's identity map depending on session configuration.
- It's coupling to internal knowledge that `matches_definition` ignores `id`.
**Suggestion:** Compare the raw fields directly without constructing a model instance:
```python
def _definitions_match(deadline_data: dict, existing: DeadlineAlertModel) -> bool:
return (
deadline_data[DeadlineAlertFields.REFERENCE] == existing.reference
and deadline_data[DeadlineAlertFields.INTERVAL] == existing.interval
and deadline_data[DeadlineAlertFields.CALLBACK] == existing.callback_def
)
```
This removes the need for `matches_definition` on the model entirely, or alternatively keeps both but makes the temp-object path unnecessary.
---
#### 3. Unnecessary alert clear+recreate when non-deadline DAG attributes change — Low
In `write_dag`, when reuse succeeds, `deadline_uuid_mapping` is set to the (non-empty) reuse dict:
```python
deadline_uuid_mapping = cls._try_reuse_deadline_uuids(...)
if deadline_uuid_mapping is not None:
dag.data["dag"]["deadline"] = existing_deadline_uuids
```
Later, in the UPDATE branch:
```python
if deadline_uuid_mapping:
updated_serialized_dag.deadline_alerts.clear()
cls._create_deadline_alert_records(updated_serialized_dag, deadline_uuid_mapping)
```
If deadlines are unchanged but another DAG attribute changes (e.g., a task is added), the hash changes, the UPDATE succeeds, and the code deletes and recreates identical deadline alert rows — a pointless write cycle.
**Fix:** After successful reuse, set `deadline_uuid_mapping = {}`:
```python
if deadline_uuid_mapping is not None:
dag.data["dag"]["deadline"] = existing_deadline_uuids
deadline_uuid_mapping = {} # alerts already exist, nothing to create
```
---
#### 4. All-or-nothing UUID regeneration — Low / Design Note
`_try_reuse_deadline_uuids` returns `None` (regenerate **all** UUIDs) if **any single** deadline definition changed. For a DAG with 3 deadlines where only one changed, the 2 unchanged deadlines also get new UUIDs and new DB rows.
Given the AIP's test matrix shows 1–3 deadlines per DAG, this is pragmatic. But if task-level deadlines are added (AIP future scope), the count could grow. Worth a `# TODO` noting the trade-off.
---
### Test Coverage
| Scenario | Covered | File |
| --- | --- | --- |
| Re-serialization preserves UUIDs | Yes | `test_dag.py:test_dag_with_multiple_deadlines` |
| Interval change triggers new SerDag + new alert | Yes | `test_serialized_dag.py:test_deadline_interval_change_triggers_new_serdag` |
| `matches_definition` — equal/different/wrong-type | Yes | `test_deadline_alert.py:test_deadline_alert_matches_definition` |
| Reference change triggers new UUIDs | **No** | — |
| Callback change triggers new UUIDs | **No** | — |
| Non-deadline DAG change with unchanged deadlines | **No** | — |
| Count mismatch (add/remove a deadline) | **No** | — |
The last four are exercised implicitly by `_try_reuse_deadline_uuids`'s logic but not explicitly tested. The reference and callback change cases are the most important gaps — if `matches_definition` regresses on those fields, only the unit test catches it, not an integration test.
---
### Summary
The core approach is sound — comparing deadline definitions to decide whether to reuse UUIDs is the right fix for the hash-churn problem. The main concerns are the `NotImplemented` return from a regular method (latent truthy-trap), the temporary ORM object construction (fragile), and a minor inefficiency in the update path. Test coverage for the happy path is solid; edge-case integration tests could be stronger.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "ferruzzi (via GitHub)" <gi...@apache.org>.
ferruzzi commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2789439642
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
Review Comment:
We should update all of those in a different PR, that file is full of every possible combination of those three letters LOL But yes, you are right I'll fix mine instead of adding more. Thanks for pointing it out.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "ferruzzi (via GitHub)" <gi...@apache.org>.
ferruzzi commented on PR #61702:
URL: https://github.com/apache/airflow/pull/61702#issuecomment-3937808653
@Dev-iL that one was a little more useful, but I'd say they were all pretty minor nitpicks. I've rebased and made some adjustments based on that feedback, if only to add comments explaining why I did it the way I did.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "ferruzzi (via GitHub)" <gi...@apache.org>.
ferruzzi commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2789452245
##########
airflow-core/tests/unit/models/test_serialized_dag.py:
##########
@@ -753,3 +763,48 @@ def test_write_dag_atomicity_on_dagcode_failure(self, dag_maker, session):
assert len(sdag.dag.task_dict) == 1, (
"SerializedDagModel should not be updated when write fails"
)
+
+ def test_deadline_interval_change_triggers_new_serdag(self, testing_dag_bundle, session):
+ dag_id = "test_interval_change"
+
+ # Create a new Dag with a deadline and create a dagrun as a baseline..
Review Comment:
Some day typos will be the only proof that it wasn't AI :P I appreciate the thorough review, I'll get all these done ASAP.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "Lee-W (via GitHub)" <gi...@apache.org>.
Lee-W commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2791041700
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -418,6 +418,68 @@ def _generate_deadline_uuids(cls, dag_data: dict[str, Any]) -> dict[str, dict]:
return uuid_mapping
+ @classmethod
+ def _try_reuse_deadline_uuids(
+ cls,
+ existing_deadline_uuids: list[str],
+ new_deadline_data: list[dict],
+ session: Session,
+ ) -> dict[str, dict] | None:
+ """
+ Try to reuse existing deadline UUIDs if the deadline definitions haven't changed.
+
+ Returns None if Deadline hashes are not all identical, indicating they need to be updated.
+
+ :param existing_deadline_uuids: List of UUID strings from existing serialized dag
+ :param new_deadline_data: List of new deadline alert data dicts from the DAG
+ :param session: Database session
+ :return: UUID mapping dict if all match, None if any mismatch detected
+ """
+ if len(existing_deadline_uuids) != len(new_deadline_data):
+ return None
+
+ existing_alerts = session.scalars(
+ select(DeadlineAlertModel).where(DeadlineAlertModel.id.in_(existing_deadline_uuids))
+ ).all()
+
+ if len(existing_alerts) != len(existing_deadline_uuids):
+ return None
+
+ new_alerts_temp = []
+ for deadline_alert in new_deadline_data:
+ deadline_data = deadline_alert.get(Encoding.VAR, deadline_alert)
+ # Create a temporary alert for comparison
+ temp_alert = DeadlineAlertModel(
+ id="temp", # id is required for the object but isn't included in the __eq__
Review Comment:
> Or perhaps I misunderstood and the point was that the custom __eq__ was what is causing confusion because someone might assume it's using object equivalence
Yes, this is how I feel and why I'm confused 🤔
> Maybe I need a comment in the code to call out that it's a custom __eq__ and not the default?
I feel like creating a new function would be less confusing.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Fix deadline alert hashing bug [airflow]
Posted by "ferruzzi (via GitHub)" <gi...@apache.org>.
ferruzzi commented on code in PR #61702:
URL: https://github.com/apache/airflow/pull/61702#discussion_r2791069498
##########
airflow-core/src/airflow/models/serialized_dag.py:
##########
@@ -503,9 +565,20 @@ def write_dag(
and existing_serialized_dag.data
and (existing_deadline_uuids := existing_serialized_dag.data.get("dag", {}).get("deadline"))
):
- dag.data["dag"]["deadline"] = existing_deadline_uuids
- deadline_uuid_mapping = {}
+ deadline_uuid_mapping = cls._try_reuse_deadline_uuids(
+ existing_deadline_uuids,
+ dag.data["dag"]["deadline"],
+ session,
+ )
+
+ if deadline_uuid_mapping is not None:
+ # All deadlines matched, reuse the UUIDs to preserve hash.
+ dag.data["dag"]["deadline"] = list(deadline_uuid_mapping.keys())
Review Comment:
Applied this in the newest revision along with a persistence issue I noticed while manually testing.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org