ferruzzi commented on code in PR #66350:
URL: https://github.com/apache/airflow/pull/66350#discussion_r3994221518


##########
airflow-core/tests/unit/utils/test_db_cleanup.py:
##########
@@ -551,6 +668,83 @@ def 
test_dag_version_cleanup_skips_versions_pinned_by_task_instance(self):
         assert latest_id in remaining  # kept by keep_last
         assert orphan_id not in remaining  # old and unreferenced -> pruned
 
+    def test_do_delete_skip_if_referenced_guards_against_race(self):
+        """_do_delete must not issue a DELETE that violates an ON DELETE 
RESTRICT FK.
+
+        Simulates a race where a dag_version row passes the SELECT filter (no 
TI
+        references it at archive-creation time) but a TI referencing it is 
inserted
+        before the DELETE runs.  The skip_if_referenced guard on the DELETE 
itself
+        must leave the row in place instead of failing with IntegrityError.
+        """
+        from airflow.utils.db import reflect_tables
+
+        base_date = pendulum.DateTime(2020, 1, 1, 
tzinfo=pendulum.timezone("UTC"))
+        bundle_name = f"race-test-{uuid4()}"
+        dag_id = f"race_dag_{uuid4()}"
+
+        with create_session() as session:
+            session.add(DagBundleModel(name=bundle_name))
+            session.flush()
+            session.add(DagModel(dag_id=dag_id, bundle_name=bundle_name))
+            session.flush()
+
+            dv = DagVersion(
+                dag_id=dag_id,
+                version_number=1,
+                bundle_name=bundle_name,
+                created_at=base_date,
+                last_updated=base_date,
+            )
+            session.add(dv)
+            session.flush()
+            dv_id = dv.id
+
+            # Manually create an archive table containing this dag_version row,
+            # simulating the CTAS step that ran before the TI was inserted.
+            # Use SQLAlchemy's Uuid codec to insert so the id encoding matches 
how
+            # DagVersion.id was stored (hyphenated native uuid on Postgres, 
.hex on
+            # MySQL/SQLite); a raw f-string would use the hyphenated form 
everywhere
+            # and match nothing on MySQL/SQLite.
+            archive_name = f"{ARCHIVE_TABLE_PREFIX}dag_version__race_test"
+            stmt = text(
+                f"CREATE TABLE {archive_name} AS SELECT * FROM dag_version 
WHERE id = :dv_id"
+            ).bindparams(bindparam("dv_id", value=dv_id, type_=Uuid()))

Review Comment:
   Tiny nit, feel free to disagree and resolve this:   I'd suggest 
`type_=DagVersion.id.type` here.  If that type ever changes, your way would 
start failing and mine would pick that change up at the source.  It may be 
overly defensive though; I can't see ever really changing it away from UUID, 
but who knows what the future brings... 



-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to