rebenitez1802 commented on code in PR #45232:
URL: https://github.com/apache/superset/pull/45232#discussion_r4274734056


##########
superset/daos/query.py:
##########
@@ -70,26 +70,50 @@ def stop_query(client_id: str) -> None:
         if not query:
             raise QueryNotFoundException(f"Query with client_id {client_id} 
not found")
 
-        if query.status in [
-            QueryStatus.FAILED,
-            QueryStatus.SUCCESS,
-            QueryStatus.TIMED_OUT,
-        ]:
+        if query.status in FINISHED_QUERY_STATUSES:
             logger.warning(
                 "Query with client_id could not be stopped: query already 
complete",
             )
             return
 
-        if not sql_lab.cancel_query(query):
+        cancelled = sql_lab.cancel_query(query)
+        if query.status in FINISHED_QUERY_STATUSES:
+            # Preparing the stop can observe the worker's terminal commit.
+            logger.warning(
+                "Query with client_id could not be stopped: query already 
complete",
+            )
+            return
+        if not cancelled:
             raise SupersetCancelQueryException("Could not cancel query")
 
+        if query.status == QueryStatus.STOPPED:
+            # The engine spec already committed the stop while preparing it
+            # (see ImpalaEngineSpec.prepare_cancel_query).
+            return
+
         # cancel_query() may have staged an early-cancel flag on query.extra
         # without committing it (see its docstring/comments); the
         # @transaction decorator commits it together with status=STOPPED
         # below in one transaction, closing the window where another
         # request could observe the flag set but the status still RUNNING.
-        query.status = QueryStatus.STOPPED
-        query.end_time = now_as_float()
+        #
+        # The write is conditional: the worker can commit a terminal status
+        # after the check above, and that result must not be overwritten.
+        stopped = (
+            db.session.query(Query)
+            .filter(
+                Query.id == query.id,
+                Query.status.notin_(FINISHED_QUERY_STATUSES),

Review Comment:
   **🔴 High — Conditional STOPPED write turns a stop into FAILED on non-Impala 
engines**
   
   This skips the STOPPED write when the row is in `FINISHED_QUERY_STATUSES`, 
and that set includes FAILED. Engines whose cancel kills the statement from 
outside the worker are affected: Postgres (`pg_terminate_backend`), MySQL 
(`KILL CONNECTION`), Snowflake, Redshift, Oracle, and Trino once the query id 
is published.
   
   - The worker's statement fails straight away, so its error handler can 
commit FAILED before this UPDATE runs.
   - The UPDATE then matches no row. The Stop request returns 200, but the 
query ends `failed` with the driver's "terminated" error in Query History and 
the API.
   - Master's unconditional write ended STOPPED whichever side committed first.
   
   We reproduced it with the 
`test_stop_query_keeps_status_committed_after_its_check` harness by having the 
worker commit FAILED inside the cancel call: the query ended `failed` at head 
and `stopped` with master's `daos/query.py`. Impala never reaches this write; 
it returns early after its own claim at `:89`.
   
   This came from my round-1 suggestion to make the write conditional; I should 
have said only statuses a stop can't cause should be protected. Suggested fix 
below. Please also add a FAILED case to `query_stop_isolation_test.py`, where 
the worker commits FAILED during `cancel_query`. Alternatively, move this 
conditional write to #45283 and keep only the Impala early return here.
   
   ```suggestion
           # The write is conditional: the worker can commit SUCCESS or 
TIMED_OUT
           # after the check above, and that result must not be overwritten. A
           # FAILED committed in this window is usually caused by the cancel
           # itself, so it is still replaced with STOPPED.
           stopped = (
               db.session.query(Query)
               .filter(
                   Query.id == query.id,
                   Query.status.notin_((QueryStatus.SUCCESS, 
QueryStatus.TIMED_OUT)),
   ```
   



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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to