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

sadpandajoe pushed a commit to branch fix-sqllab-async-limit-race
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 29374482b199355962866ff267612362618f3084
Author: Joe Li <[email protected]>
AuthorDate: Sat Oct 10 19:31:06 2026 +0000

    fix(sqllab): commit the recomputed row limit before async dispatch
    
    ExecuteSqlCommand recomputes query.limit as min(the query's own SQL
    LIMIT, the row-limit dropdown) and updates it in place, but never
    commits before ASynchronousSqlJsonExecutor hands the query off to a
    Celery worker via .delay(). @transaction() on run() only commits after
    the whole method returns, which can be after an idle worker has
    already re-fetched the query on its own connection. Under READ
    COMMITTED, that worker then sees the row's limit as it was at creation
    time -- the raw dropdown value -- instead of the smaller value just
    computed from the query's own LIMIT, so the dropdown silently wins
    whenever it is the larger of the two. Same mechanism as #40070.
---
 superset/commands/sql_lab/execute.py | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/superset/commands/sql_lab/execute.py 
b/superset/commands/sql_lab/execute.py
index 99ed73fdef7..e2ada480438 100644
--- a/superset/commands/sql_lab/execute.py
+++ b/superset/commands/sql_lab/execute.py
@@ -164,6 +164,18 @@ class ExecuteSqlCommand(BaseCommand):
             self._query_dao.update(
                 query, {"limit": self._execution_context.query.limit}
             )
+            # Commit now, before dispatch. The async executor below hands off 
to
+            # a Celery worker that re-fetches this query on its own connection
+            # (`get_query` in superset/sql_lab.py); under READ COMMITTED that
+            # worker only sees the limit recomputed just above once it is
+            # committed. `@transaction()` on `run()` only commits after this
+            # whole method returns, which is too late: `.delay()` can hand the
+            # task to an already-idle worker before that commit lands, so the
+            # worker reads back the row's limit as it was at creation time (the
+            # raw dropdown value from `create_query()`) instead of the smaller
+            # value just computed from the query's own SQL LIMIT. Same race and
+            # fix as apache/superset#40070.
+            db.session.commit()  # pylint: disable=consider-using-transaction
             return self._sql_json_executor.execute(
                 self._execution_context, rendered_query, self._log_params
             )

Reply via email to