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

amoghrajesh 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 669579e6c84 Fix SnowflakeSqlApiOperator polling to avoid needless 
sleeps (#69450)
669579e6c84 is described below

commit 669579e6c844dc6b73d2acacb411b1e57b860eb9
Author: Amogh Desai <[email protected]>
AuthorDate: Mon Jul 6 18:34:26 2026 +0530

    Fix SnowflakeSqlApiOperator polling to avoid needless sleeps (#69450)
---
 .../providers/snowflake/operators/snowflake.py     |  8 +++-
 .../unit/snowflake/operators/test_snowflake.py     | 51 ++++++++++++++++++++--
 2 files changed, 53 insertions(+), 6 deletions(-)

diff --git 
a/providers/snowflake/src/airflow/providers/snowflake/operators/snowflake.py 
b/providers/snowflake/src/airflow/providers/snowflake/operators/snowflake.py
index eec2e39ecc2..b9e74b18589 100644
--- a/providers/snowflake/src/airflow/providers/snowflake/operators/snowflake.py
+++ b/providers/snowflake/src/airflow/providers/snowflake/operators/snowflake.py
@@ -491,11 +491,15 @@ class SnowflakeSqlApiOperator(SQLExecuteQueryOperator):
             if statement_status.get("status") == "error":
                 queries_in_progress.remove(query_id)
                 statement_error_status[query_id] = statement_status
-            if statement_status.get("status") == "success":
+            elif statement_status.get("status") == "success":
                 statement_success_status[query_id] = statement_status
                 queries_in_progress.remove(query_id)
-            if statement_status.get("status") == "running":
+            elif statement_status.get("status") == "running":
                 statement_running_status[query_id] = statement_status
+        # Only wait before the next poll cycle if something is still running. 
Sleeping
+        # unconditionally after every handle would delay returning even when 
this cycle
+        # already resolved everything (e.g. all statements finished, or one 
failed).
+        if queries_in_progress:
             time.sleep(self.poll_interval)
         return {
             "success": statement_success_status,
diff --git 
a/providers/snowflake/tests/unit/snowflake/operators/test_snowflake.py 
b/providers/snowflake/tests/unit/snowflake/operators/test_snowflake.py
index cea968c38cd..b1d6b418839 100644
--- a/providers/snowflake/tests/unit/snowflake/operators/test_snowflake.py
+++ b/providers/snowflake/tests/unit/snowflake/operators/test_snowflake.py
@@ -359,6 +359,47 @@ class TestSnowflakeSqlApiOperator:
         with pytest.raises(RuntimeError, match="Failed to get status for query 
uuid1"):
             operator.poll_on_queries()
 
+    def test_poll_on_queries_no_sleep_when_all_resolved(self, 
mock_get_sql_api_query_status):
+        operator = SnowflakeSqlApiOperator(
+            task_id=TASK_ID,
+            snowflake_conn_id="snowflake_default",
+            sql=SQL_MULTIPLE_STMTS,
+            statement_count=4,
+            do_xcom_push=False,
+        )
+        operator.query_ids = ["uuid1", "uuid2"]
+        mock_get_sql_api_query_status.side_effect = [{"status": "success"}, 
{"status": "error"}]
+
+        with mock.patch("time.sleep") as mock_sleep:
+            result = operator.poll_on_queries()
+
+        mock_sleep.assert_not_called()
+        assert result["success"] == {"uuid1": {"status": "success"}}
+        assert result["error"] == {"uuid2": {"status": "error"}}
+        assert result["running"] == {}
+
+    def test_poll_on_queries_sleeps_once_per_cycle(self, 
mock_get_sql_api_query_status):
+        """One handle is still running, so the cycle sleeps -- but only once, 
not per handle."""
+        operator = SnowflakeSqlApiOperator(
+            task_id=TASK_ID,
+            snowflake_conn_id="snowflake_default",
+            sql=SQL_MULTIPLE_STMTS,
+            statement_count=4,
+            do_xcom_push=False,
+        )
+        operator.query_ids = ["uuid1", "uuid2", "uuid3"]
+        mock_get_sql_api_query_status.side_effect = [
+            {"status": "success"},
+            {"status": "running"},
+            {"status": "success"},
+        ]
+
+        with mock.patch("time.sleep") as mock_sleep:
+            result = operator.poll_on_queries()
+
+        mock_sleep.assert_called_once_with(operator.poll_interval)
+        assert result["running"] == {"uuid2": {"status": "running"}}
+
     @pytest.mark.parametrize(
         ("mock_sql", "statement_count"),
         [pytest.param(SQL_MULTIPLE_STMTS, 4, id="multi"), 
pytest.param(SINGLE_STMT, 1, id="single")],
@@ -581,18 +622,20 @@ class TestSnowflakeSqlApiOperator:
         mock_get_sql_api_query_status.side_effect = [
             # Initial get_sql_api_query_status check
             {"status": "running"},
-            # 1st poll_on_queries check (poll_interval: 5s)
+            # 1st poll_on_queries check (poll_interval: 5s) -- still running, 
sleeps
             {"status": "running"},
-            # 2nd poll_on_queries check (poll_interval: 5s)
+            # 2nd poll_on_queries check (poll_interval: 5s) -- still running, 
sleeps
             {"status": "running"},
-            # 3rd poll_on_queries check (poll_interval: 5s)
+            # 3rd poll_on_queries check -- resolves to success, no sleep needed
             {"status": "success"},
         ]
 
         with mock.patch("time.sleep") as mock_sleep:
             operator.execute(context=None)
             mock_check_query_output.assert_called_once_with(["uuid1"])
-            assert mock_sleep.call_count == 3
+            # Only 2 sleeps: the cycle that resolves the last running query 
returns
+            # immediately instead of sleeping once more before reporting 
success.
+            assert mock_sleep.call_count == 2
 
     def test_snowflake_sql_api_execute_operator_polling_failed(
         self, mock_execute_query, mock_get_sql_api_query_status, 
mock_check_query_output

Reply via email to