This is an automated email from the ASF dual-hosted git repository.

shahar1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new a1e66b1f1d3 Check hooks before cancelling BigQuery jobs in on_kill 
(#74946)
a1e66b1f1d3 is described below

commit a1e66b1f1d3906b161889052555c2735f98466b4
Author: Yuseok Jo <[email protected]>
AuthorDate: Sun Oct 18 16:50:47 2026 +0900

    Check hooks before cancelling BigQuery jobs in on_kill (#74946)
---
 .../providers/google/cloud/operators/bigquery.py   |  6 ++----
 .../google/cloud/transfers/bigquery_to_gcs.py      |  6 ++----
 .../unit/google/cloud/operators/test_bigquery.py   | 12 +++++++++++
 .../google/cloud/transfers/test_bigquery_to_gcs.py | 25 ++++++++++++++++------
 4 files changed, 34 insertions(+), 15 deletions(-)

diff --git 
a/providers/google/src/airflow/providers/google/cloud/operators/bigquery.py 
b/providers/google/src/airflow/providers/google/cloud/operators/bigquery.py
index 8b60e461ead..76c2389e3c5 100644
--- a/providers/google/src/airflow/providers/google/cloud/operators/bigquery.py
+++ b/providers/google/src/airflow/providers/google/cloud/operators/bigquery.py
@@ -2708,10 +2708,8 @@ class BigQueryInsertJobOperator(
         return self.job_id
 
     def on_kill(self) -> None:
-        if self.job_id and self.cancel_on_kill:
-            self.hook.cancel_job(  # type: ignore[union-attr]
-                job_id=self.job_id, project_id=self.project_id, 
location=self.location
-            )
+        if self.hook and self.job_id and self.cancel_on_kill:
+            self.hook.cancel_job(job_id=self.job_id, 
project_id=self.project_id, location=self.location)
         else:
             self.log.info("Skipping to cancel job: %s:%s.%s", self.project_id, 
self.location, self.job_id)
 
diff --git 
a/providers/google/src/airflow/providers/google/cloud/transfers/bigquery_to_gcs.py
 
b/providers/google/src/airflow/providers/google/cloud/transfers/bigquery_to_gcs.py
index a15a6eb08b7..db5b40b8cc6 100644
--- 
a/providers/google/src/airflow/providers/google/cloud/transfers/bigquery_to_gcs.py
+++ 
b/providers/google/src/airflow/providers/google/cloud/transfers/bigquery_to_gcs.py
@@ -304,10 +304,8 @@ class BigQueryToGCSOperator(BaseOperator):
         return self.destination_cloud_storage_uris
 
     def on_kill(self) -> None:
-        if self.job_id and self.cancel_on_kill:
-            self.hook.cancel_job(  # type: ignore[union-attr]
-                job_id=self.job_id, project_id=self.project_id, 
location=self.location
-            )
+        if self.hook and self.job_id and self.cancel_on_kill:
+            self.hook.cancel_job(job_id=self.job_id, 
project_id=self.project_id, location=self.location)
         else:
             self.log.info("Skipping to cancel job: %s:%s.%s", self.project_id, 
self.location, self.job_id)
 
diff --git 
a/providers/google/tests/unit/google/cloud/operators/test_bigquery.py 
b/providers/google/tests/unit/google/cloud/operators/test_bigquery.py
index c44947bf1f6..86172a145f0 100644
--- a/providers/google/tests/unit/google/cloud/operators/test_bigquery.py
+++ b/providers/google/tests/unit/google/cloud/operators/test_bigquery.py
@@ -1200,6 +1200,18 @@ class TestBigQueryInsertJobOperator:
             project_id=TEST_GCP_PROJECT_ID,
         )
 
+    def test_on_kill_before_execute(self):
+        op = BigQueryInsertJobOperator(
+            task_id="insert_query_job",
+            configuration={"query": {"query": "SELECT * FROM any", 
"useLegacySql": False}},
+            location=TEST_DATASET_LOCATION,
+            job_id="123456",
+            project_id=TEST_GCP_PROJECT_ID,
+            durable=False,
+        )
+
+        op.on_kill()
+
     
@mock.patch("airflow.providers.google.cloud.operators.bigquery.BigQueryHook")
     @mock.patch("airflow.providers.google.cloud.hooks.bigquery.BigQueryJob")
     def test_on_kill_after_execution_timeout(self, mock_job, mock_hook):
diff --git 
a/providers/google/tests/unit/google/cloud/transfers/test_bigquery_to_gcs.py 
b/providers/google/tests/unit/google/cloud/transfers/test_bigquery_to_gcs.py
index 1231375beb8..c9c28f3366f 100644
--- a/providers/google/tests/unit/google/cloud/transfers/test_bigquery_to_gcs.py
+++ b/providers/google/tests/unit/google/cloud/transfers/test_bigquery_to_gcs.py
@@ -209,8 +209,9 @@ class TestBigQueryToGCSOperator:
 
         assert exc.value.trigger.cancel_on_kill is False
 
+    @pytest.mark.parametrize("cancel_on_kill", [True, False])
     
@mock.patch("airflow.providers.google.cloud.transfers.bigquery_to_gcs.BigQueryHook")
-    def test_on_kill(self, mock_hook):
+    def test_on_kill(self, mock_hook, cancel_on_kill):
         mock_hook.return_value.split_tablename.return_value = (PROJECT_ID, 
TEST_DATASET, TEST_TABLE_ID)
         mock_hook.return_value.insert_job.return_value = 
MagicMock(job_id="real_job_id", error_result=False)
 
@@ -220,19 +221,29 @@ class TestBigQueryToGCSOperator:
             destination_cloud_storage_uris=["gs://some-bucket/some-file.txt"],
             project_id=JOB_PROJECT_ID,
             location="US",
-            cancel_on_kill=False,
+            cancel_on_kill=cancel_on_kill,
         )
         operator.execute(context=mock.MagicMock())
 
         operator.on_kill()
-        mock_hook.return_value.cancel_job.assert_not_called()
 
-        operator.cancel_on_kill = True
-        operator.on_kill()
-        mock_hook.return_value.cancel_job.assert_called_once_with(
-            job_id="real_job_id", project_id=JOB_PROJECT_ID, location="US"
+        if cancel_on_kill:
+            mock_hook.return_value.cancel_job.assert_called_once_with(
+                job_id="real_job_id", project_id=JOB_PROJECT_ID, location="US"
+            )
+        else:
+            mock_hook.return_value.cancel_job.assert_not_called()
+
+    def test_on_kill_before_execute(self):
+        operator = BigQueryToGCSOperator(
+            task_id=TASK_ID,
+            
source_project_dataset_table=f"{PROJECT_ID}:{TEST_DATASET}.{TEST_TABLE_ID}",
+            destination_cloud_storage_uris=["gs://some-bucket/some-file.txt"],
+            job_id="given_job_id",
         )
 
+        operator.on_kill()
+
     def test_execute_complete_reassigns_job_id(self):
         """Assert that we use job_id from event after deferral."""
 

Reply via email to