I3eka commented on code in PR #45561:
URL: https://github.com/apache/superset/pull/45561#discussion_r4377042599


##########
superset/sql/execution/executor.py:
##########
@@ -680,16 +684,24 @@ def execute(
                 QueryStatus.TIMED_OUT,
                 "Query exceeded the timeout limit",
                 start_time,
+                query=query,
             )
         except SupersetSecurityException as ex:
-            return self._create_error_result(QueryStatus.FAILED, str(ex), 
start_time)
-        except (OAuth2RedirectError, OAuth2Error):
+            return self._create_error_result(
+                QueryStatus.FAILED, str(ex), start_time, query=query
+            )
+        except (OAuth2RedirectError, OAuth2Error) as ex:
             # Let OAuth2 exceptions propagate so callers (MCP, API) can
             # handle them with context-appropriate responses.
+            self._create_error_result(
+                QueryStatus.FAILED, str(ex), start_time, query=query
+            )
             raise
         except Exception as ex:
             error_msg = self.database.db_engine_spec.extract_error_message(ex)
-            return self._create_error_result(QueryStatus.FAILED, error_msg, 
start_time)
+            return self._create_error_result(
+                QueryStatus.FAILED, error_msg, start_time, query=query
+            )

Review Comment:
   Fixed in 8698a1afe6. Cache writes now catch and log failures inside 
`_store_in_cache()`, so a cache outage cannot replace the successful SQL result 
or rewrite the committed Query as failed. The regression forces 
`data_cache.set()` to raise and checks both the returned rows and the stored 
success state through a fresh metadata session.



##########
superset/sql/execution/executor.py:
##########
@@ -1008,14 +1021,41 @@ def _create_error_result(
         :param start_time: Start time for calculating execution duration
         :param partial_results: Optional list of StatementResult from 
successful
             statements before the failure
+        :param query: Persisted audit record, if execution reached that stage
         :returns: QueryResult with error status
         """
-        from superset_core.queries.types import QueryResult as QueryResultType
+        from superset_core.queries.types import (
+            QueryResult as QueryResultType,
+            QueryStatus,
+        )
+
+        from superset.sql.execution.utils import commit_query_unless_stopped
+
+        query_id = None
+        if query is not None:
+            try:
+                # Read the persisted identity without loading expired 
attributes.
+                query_id = inspect(query).identity[0]
+                # A failed metadata flush must be rolled back before recording 
failure.
+                if not db.session.is_active:
+                    db.session.rollback()  # pylint: 
disable=consider-using-transaction
+                query.status = status.value
+                query.error_message = error_message
+                query.end_time = now_as_float()
+                if not commit_query_unless_stopped(query):
+                    status = QueryStatus.STOPPED

Review Comment:
   Fixed in 8698a1afe6. The STOPPED branch now commits after reading the 
winning status and error message, releasing the transaction opened by the 
guarded refresh. The concurrent-Stop regression asserts `not 
session.in_transaction()` before its later caller commit, then checks the 
preserved status, end time, and cancellation metadata from another session. The 
success path uses the same guard and closes its STOPPED transaction too.



##########
superset/sql/execution/executor.py:
##########
@@ -1008,14 +1021,41 @@ def _create_error_result(
         :param start_time: Start time for calculating execution duration
         :param partial_results: Optional list of StatementResult from 
successful
             statements before the failure
+        :param query: Persisted audit record, if execution reached that stage
         :returns: QueryResult with error status
         """
-        from superset_core.queries.types import QueryResult as QueryResultType
+        from superset_core.queries.types import (
+            QueryResult as QueryResultType,
+            QueryStatus,
+        )
+
+        from superset.sql.execution.utils import commit_query_unless_stopped
+
+        query_id = None
+        if query is not None:
+            try:
+                # Read the persisted identity without loading expired 
attributes.
+                query_id = inspect(query).identity[0]
+                # A failed metadata flush must be rolled back before recording 
failure.
+                if not db.session.is_active:
+                    db.session.rollback()  # pylint: 
disable=consider-using-transaction
+                query.status = status.value
+                query.error_message = error_message

Review Comment:
   Fixed in 8698a1afe6: failure finalization now uses `query.end_time or 
now_as_float()`. Loading an expired timestamp is wrapped in `no_autoflush` so 
it cannot flush staged failure metadata before the Stop guard. A regression 
verifies that an existing end time is preserved.



##########
superset/sql/execution/executor.py:
##########
@@ -680,16 +683,24 @@ def execute(
                 QueryStatus.TIMED_OUT,
                 "Query exceeded the timeout limit",
                 start_time,
+                query=query,
             )
         except SupersetSecurityException as ex:
-            return self._create_error_result(QueryStatus.FAILED, str(ex), 
start_time)
-        except (OAuth2RedirectError, OAuth2Error):
+            return self._create_error_result(
+                QueryStatus.FAILED, str(ex), start_time, query=query
+            )
+        except (OAuth2RedirectError, OAuth2Error) as ex:
             # Let OAuth2 exceptions propagate so callers (MCP, API) can
             # handle them with context-appropriate responses.
+            self._create_error_result(
+                QueryStatus.FAILED, str(ex), start_time, query=query
+            )
             raise

Review Comment:
   Rechecked this on 8698a1afe6. Failure recording catches metadata commit 
errors and suppresses recovery-rollback errors; the OAuth handlers still 
re-raise the original exception. All eight SQL/timeout/OAuth error and redirect 
cases pass, including checks that callers receive the same OAuth exception 
object when persistence fails.



##########
superset/sql/execution/executor.py:
##########
@@ -1008,14 +1020,24 @@ def _create_error_result(
         :param start_time: Start time for calculating execution duration
         :param partial_results: Optional list of StatementResult from 
successful
             statements before the failure
+        :param query: Persisted audit record, if execution reached that stage
         :returns: QueryResult with error status
         """
         from superset_core.queries.types import QueryResult as QueryResultType
 
+        if query is not None:
+            # A failed metadata flush must be rolled back before recording 
failure.
+            db.session.rollback()  # pylint: disable=consider-using-transaction
+            query.status = status.value

Review Comment:
   Rechecked and completed in 8698a1afe6. Error finalization uses 
`commit_query_unless_stopped()`; the winning Stop retains its status, 
timestamp, and cancellation metadata. The False branch now also closes its 
transaction, and success publication uses the same guard. Separate SQLite 
sessions reproduce Stop during failure finalization and during the final fetch.



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