From 417ca955e2af0e77ed76cb13c704391adf0c00c9 Mon Sep 17 00:00:00 2001 From: Steve Ahn Date: Tue, 13 Jan 2026 08:53:11 -0800 Subject: [PATCH] [v3-1-test] Unique run_id across manually triggered Dags with schedules (#59477) * run id no longer collides in manual runs * ruff format; (cherry picked from commit 13f3e267fb85970de7b25bf41019fcfade8ba014) Co-authored-by: Steve Ahn --- .../core_api/datamodels/dag_run.py | 5 +-- .../core_api/routes/public/test_dag_run.py | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dag_run.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dag_run.py index 96c3a3a13ad0f..1160842dd2c64 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dag_run.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dag_run.py @@ -123,10 +123,7 @@ def validate_context(self, dag: SerializedDAG) -> dict: end=timezone.coerce_datetime(self.data_interval_end), ) else: - data_interval = dag.timetable.infer_manual_data_interval( - run_after=coerced_logical_date or timezone.coerce_datetime(run_after) - ) - run_after = data_interval.end + data_interval = dag.timetable.infer_manual_data_interval(run_after=coerced_logical_date) run_id = self.dag_run_id or dag.timetable.generate_run_id( run_type=DagRunType.MANUAL, diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py index 47baca3046f6f..63088ba4f393d 100644 --- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py +++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py @@ -1836,6 +1836,41 @@ def test_should_respond_200_with_null_logical_date(self, test_client): "note": None, } + @pytest.mark.usefixtures("configure_git_connection_for_dag_bundle") + def test_should_generate_unique_run_id_for_scheduled_dag(self, dag_maker, test_client, session): + "Ensure manual triggers on scheduled DAGs don't conflict on run_id" + scheduled_dag_id = "test_scheduled_dag" + with dag_maker( + dag_id=scheduled_dag_id, + schedule="@daily", + start_date=START_DATE1, + session=session, + serialized=True, + ): + EmptyOperator(task_id="test_task") + + session.commit() + + response_1 = test_client.post( + f"/dags/{scheduled_dag_id}/dagRuns", + json={ + "logical_date": "2025-12-11T16:00:00+00:00", + "run_after": "2025-12-11T16:00:00+00:00", + }, + ) + assert response_1.status_code == 200 + + response_2 = test_client.post( + f"/dags/{scheduled_dag_id}/dagRuns", + json={ + "logical_date": "2025-12-11T16:01:00+00:00", + "run_after": "2025-12-11T16:01:00+00:00", + }, + ) + assert response_2.status_code == 200 + + assert response_1.json()["dag_run_id"] != response_2.json()["dag_run_id"] + @time_machine.travel("2025-10-02 12:00:00", tick=False) @pytest.mark.usefixtures("custom_timetable_plugin") def test_custom_timetable_generate_run_id_for_manual_trigger(self, dag_maker, test_client, session):