aaron-y-chen commented on code in PR #72809:
URL: https://github.com/apache/airflow/pull/72809#discussion_r4023666984


##########
providers/google/src/airflow/providers/google/cloud/hooks/dataflow.py:
##########
@@ -339,6 +339,15 @@ def _get_current_jobs(self) -> list[dict]:
             jobs = self._fetch_jobs_by_prefix_name(self._job_name.lower())
             if len(jobs) == 1:
                 self._job_id = jobs[0]["id"]
+            elif len(jobs) > 1 and not self._multiple_jobs:
+                active_jobs = [
+                    job for job in jobs if job.get("currentState") not in 
DataflowJobStatus.TERMINAL_STATES
+                ]
+                if len(active_jobs) == 1:
+                    self._job_id = active_jobs[0]["id"]
+                else:
+                    jobs.sort(key=lambda j: j.get("createTime", ""), 
reverse=True)
+                    self._job_id = jobs[0]["id"]

Review Comment:
   With `append_job_name=False`, the [job name stays the 
same](https://github.com/apache/airflow/blob/be95313ae7d67eb04868f381f61087df62df2ec5/providers/google/src/airflow/providers/google/cloud/hooks/dataflow.py#L1066-L1069)
 for every run.
   
   For example, this Dag could hit the issue.
   ```python
   BeamRunPythonPipelineOperator(
       task_id="run_beam",
       runner="DataflowRunner",
       py_file="gs://my-bucket/etl.py",
       deferrable=True,
       dataflow_config=DataflowConfiguration(
           job_name="daily-etl",
           append_job_name=False,
           location="us-central1",
       ),
   )
   ```
   
   Step 1. 
   The lookup runs at the first [`select` 
timeout](https://github.com/apache/airflow/blob/be95313ae7d67eb04868f381f61087df62df2ec5/providers/apache/beam/src/airflow/providers/apache/beam/hooks/beam.py#L184)(5
 seconds), before this run's job is created, since staging takes much longer 
than that.
   
   So `_fetch_jobs_by_prefix_name()` returns every job in the region, including 
finished ones, and keeps the ones whose name starts with `daily-etl`, like the 
jobs from the past two days. `jobs` would look something like this:
   ```python
   jobs = [
     {"id": "2026-09-14_03_00_12-1182736549827",
      "name": "daily-etl",              "currentState": "JOB_STATE_DONE",    
"createTime": "2026-09-14T03:00:12Z"},
     {"id": "2026-09-15_03_00_09-9938174462051",
      "name": "daily-etl",              "currentState": "JOB_STATE_DONE",    
"createTime": "2026-09-15T03:00:09Z"}
   ]
   ```
   
   Step 2.
   Since both are in `DataflowJobStatus.TERMINAL_STATES`, `active_jobs` ends up 
empty, so we fall nto the `else` branch and `self._job_id` gets yesterday's job:
   ```python
   else:
       jobs.sort(key=lambda j: j.get("createTime", ""), reverse=True)
       #   "2026-09-15T03:00:09Z"
       #   "2026-09-14T03:00:12Z"
       self._job_id = jobs[0]["id"]
       #   = "2026-09-15_03_00_09-9938174462051"   <- yesterday's finished job
   ```
   
   Neither of these jobs is active, so the `DataflowJobAlreadyExistsError` rule 
doesn't prevent this. The operator then defers on a job that already reached 
`JOB_STATE_DONE`, while the process that is still submitting this run's job is 
dropped.
   
   Would it make sense to only accept jobs created after this task started, and 
keep waiting when every match is terminal?



-- 
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