superset icon indicating copy to clipboard operation
superset copied to clipboard

fix(explore): stop button cancels running chart queries in the backend

Open X-arshiya-X opened this issue 3 weeks ago • 13 comments

User description

SUMMARY

This PR fixes Issue #35347, where the Stop button in Chart and Dashboard views did not actually stop running queries on Trino and other databases.

Previously:

  • The stop button in the Explore/Chart view was not wired to the backend cancellation logic.
  • Chart queries were effectively stateless from the perspective of cancellation and did not carry query_id / client tracking information used by the backend to kill queries.
  • As a result, clicking Stop, closing the tab, refreshing the page, or navigating away would still leave the query running on the database, consuming unnecessary compute resources.

This PR:

  • Adds client and query tracking for chart queries so they carry the necessary identifiers for cancellation.
  • Wires the chart stop button to a new backend API endpoint that uses the same query-killing logic already used by SQL Lab.
  • Ensures that when a user clicks Stop in Chart view, a cancellation request is sent to the backend, which then kills the corresponding running query on the database.

Key changes include:

Frontend:

  • superset-frontend/src/components/Chart/chartAction.js
  • superset-frontend/src/components/Chart/chartReducer.ts
  • superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
  • superset-frontend/src/explore/exploreUtils/index.js
  • superset-frontend/src/explore/types.ts

These changes add client/query tracking to chart queries and connect the Stop button to the new cancellation API.

Backend:

  • superset/charts/schemas.py
  • superset/common/query_context.py
  • superset/common/query_context_factory.py
  • superset/common/query_context_processor.py
  • superset/models/core.py
  • superset/models/helpers.py

These changes ensure that chart queries are created with client/query identifiers and that the backend can locate and cancel the corresponding running query.

Behaviorally, there are no breaking changes: the Stop button now behaves as expected by users (similar to SQL Lab), and no existing functionality is regressed.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

No visual UI changes. The Stop button remains the same visually; only its behavior is corrected (it now kills the backend query).

Behavioral demonstration:

  • Before: Clicking Stop in Chart view would stop the frontend loading indicator, but the query continued running in the database (e.g., Trino), wasting resources.
image1 image2
  • After: Clicking Stop in Chart view sends a cancellation request to the backend, which kills the query in the database. The query transitions to a cancelled/killed state (e.g., USER ERROR: ADMINISTRATIVELY KILLED in Trino).
image3

TESTING INSTRUCTIONS

  1. Set up Superset with Trino (or another supported DB where you can see running queries).
  2. Start Superset and log in.
  3. Go to Explore / Create Chart and configure a chart that triggers a long-running query (e.g., a heavy query against Trino).
  4. Open the Trino UI (or the database's query monitoring UI) to observe running queries.
  5. In Chart view, run the chart and confirm the query appears as running in Trino.
  6. Click the Stop button in Chart view.
  7. Verify in the Trino UI that:
    • The query is cancelled, and
    • It transitions to an error such as USER ERROR: ADMINISTRATIVELY KILLED (or the equivalent cancellation message for your database).
  8. Repeat the above steps from a Dashboard that contains a chart to confirm that stopping from the Dashboard view also cancels the underlying query.

Additionally:

  • Run the existing test suite to ensure no regressions:
    • pytest (backend)
    • npm test / pnpm test (frontend), as appropriate for the repo setup.
  • Confirm that SQL Lab query cancellation behavior remains unchanged and still works as expected (regression check).

ADDITIONAL INFORMATION

  • [x] Has associated issue: Fixes #35347
  • [ ] Required feature flags:
  • [ ] Changes UI
  • [ ] Includes DB Migration (follow approval process in SIP-59)
    • [ ] Migration is atomic, supports rollback & is backwards-compatible
    • [ ] Confirm DB migration upgrade and downgrade tested
    • [ ] Runtime estimates and downtime expectations provided
  • [x] Introduces new feature or API
  • [ ] Removes existing feature or API

Additional notes:

  • No new external dependencies are introduced.
  • No performance concerns identified; cancelling queries should reduce unnecessary resource usage.
  • No DB migrations are required.
  • The change reuses the existing SQL Lab query cancellation mechanism, ensuring consistent behavior across SQL Lab and Explore/Chart views.

Contributors:


CodeAnt-AI Description

Make the Explore chart Stop button cancel running database queries

What Changed

  • Clicking Stop in Explore now cancels the underlying database query, not just the in-flight HTTP request
  • Each chart query now includes a client-generated id so the backend can track and stop that specific query
  • Query cancellation is more robust across databases, reducing cases where a stopped chart leaves work running in the database
  • If the backend fails to stop a query, the UI shows a clear toast error instead of failing silently

Impact

✅ Chart queries can be reliably cancelled from Explore ✅ Fewer abandoned long-running chart queries after clicking Stop or navigating away ✅ Lower database load from cancelled or abandoned chart runs

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

X-arshiya-X avatar Nov 30 '25 23:11 X-arshiya-X

/review

Vab-170 avatar Dec 01 '25 00:12 Vab-170

/review

Vab-170 avatar Dec 01 '25 01:12 Vab-170

Code Review Agent Run #1b36c1

Actionable Suggestions - 0
Additional Suggestions - 7
  • superset/models/core.py - 1
    • Use logging instead of print · Line 825-831
      The added debug output uses print(), which sends to stdout and can interfere with production logging systems. Replace with logger.debug() using the existing logger instance for consistent debug handling.
      Code suggestion
       @@ -824,8 +824,1 @@
      -        try:
      -            print(
      -                f"[Database.get_df] called for database_id={getattr(self, 'id', None)} "
      -                f"query_id={getattr(query, 'id', None) if query is not None else None} "
      -                f"client_id={getattr(query, 'client_id', None) if query is not None else None}"
      -            )
      -        except Exception:
      -            logger.debug("Could not print Database.get_df debug info", exc_info=True)
      +        logger.debug(
      +            f"[Database.get_df] called for database_id={getattr(self, 'id', None)} "
      +            f"query_id={getattr(query, 'id', None) if query is not None else None} "
      +            f"client_id={getattr(query, 'client_id', None) if query is not None else None}"
      +        )
      
  • superset-frontend/src/explore/components/ExploreViewContainer/index.jsx - 1
    • Inconsistent Error Handling · Line 414-416
      The error handling here uses console.error, but SqlLab's equivalent code uses addDangerToast for user-visible notifications. Consider updating to props.addDangerToast('Failed to stop query.') to provide consistent UX across the app.
      Code suggestion
       @@ -414,3 +414,3 @@
      -      }).catch(error => {
      -        console.error('Error stopping query on backend:', error);
      -      });
      +      }).catch(error => {
      +        props.addDangerToast('Failed to stop query.');
      +      });
      
  • superset-frontend/src/explore/exploreUtils/index.js - 1
    • Remove debug console.log · Line 243-243
      The console.log on line 243 appears to be a debug statement left in production code. According to the contributing guidelines, debug statements like this should be removed to keep the codebase clean.
  • superset/sql_lab.py - 2
    • Logging Level Too Low · Line 687-690
      The logging level for prepare_cancel_query failures is set to debug, which may not be enabled in production environments. Consider upgrading to warning level for better observability of potential database preparation issues.
      Code suggestion
       @@ -690,1 +690,1 @@
      -        logger.debug("prepare_cancel_query failed", exc_info=True)
      +        logger.warning("prepare_cancel_query failed", exc_info=True)
      
    • Inconsistent Error Logging · Line 712-718
      cancel_query exceptions are caught but not logged, unlike prepare_cancel_query. This inconsistency makes debugging cancel failures harder. Consider adding warning-level logging here too.
  • superset-frontend/src/components/Chart/chartAction.js - 2
    • Lint violation: console statement · Line 386-386
      This console.log statement violates the project's lint rules against console statements in production code. It should be removed to maintain code quality standards.
      Code suggestion
       @@ -384,6 +384,2 @@
      -  export function handleChartDataResponse(response, json, useLegacyApi) {
      - 
      -  console.log('handleChartDataResponse', response, json, useLegacyApi);
      -  // todo: hopefully handle responses of chart view
      - 
      -  if (isFeatureEnabled(FeatureFlag.GlobalAsyncQueries)) {
      +  export function handleChartDataResponse(response, json, useLegacyApi) {
      +  // todo: hopefully handle responses of chart view
      +  if (isFeatureEnabled(FeatureFlag.GlobalAsyncQueries)) {
      
    • Lint violation: console statement · Line 438-438
      This console.log statement violates the project's lint rules against console statements in production code. It should be removed to maintain code quality standards.
      Code suggestion
       @@ -437,4 +437,3 @@
      -    const clientId = nanoid(11);
      -    console.log('Generated client_id for chart query:', clientId);
      -    
      -    dispatch(chartUpdateStarted(controller, formData, key, clientId));
      +    const clientId = nanoid(11);
      +    
      +    dispatch(chartUpdateStarted(controller, formData, key, clientId));
      
Review Details
  • Files reviewed - 14 · Commit Range: 7506007..ea0d1ff
    • superset-frontend/src/components/Chart/chartAction.js
    • superset-frontend/src/components/Chart/chartReducer.ts
    • superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
    • superset-frontend/src/explore/exploreUtils/index.js
    • superset-frontend/src/explore/types.ts
    • superset/charts/schemas.py
    • superset/common/query_context.py
    • superset/common/query_context_factory.py
    • superset/common/query_context_processor.py
    • superset/connectors/sqla/models.py
    • superset/daos/query.py
    • superset/models/core.py
    • superset/models/helpers.py
    • superset/sql_lab.py
  • Files skipped - 0
  • Tools
    • Eslint (Linter) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at [email protected].

Documentation & Help

AI Code Review powered by Bito Logo

bito-code-review[bot] avatar Dec 01 '25 01:12 bito-code-review[bot]

/review

X-arshiya-X avatar Dec 01 '25 01:12 X-arshiya-X

/review

X-arshiya-X avatar Dec 01 '25 01:12 X-arshiya-X

Code Review Agent Run #bbca53

Actionable Suggestions - 0
Review Details
  • Files reviewed - 14 · Commit Range: 7506007..37d2e5e
    • superset-frontend/src/components/Chart/chartAction.js
    • superset-frontend/src/components/Chart/chartReducer.ts
    • superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
    • superset-frontend/src/explore/exploreUtils/index.js
    • superset-frontend/src/explore/types.ts
    • superset/charts/schemas.py
    • superset/common/query_context.py
    • superset/common/query_context_factory.py
    • superset/common/query_context_processor.py
    • superset/connectors/sqla/models.py
    • superset/daos/query.py
    • superset/models/core.py
    • superset/models/helpers.py
    • superset/sql_lab.py
  • Files skipped - 0
  • Tools
    • Eslint (Linter) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at [email protected].

Documentation & Help

AI Code Review powered by Bito Logo

bito-code-review[bot] avatar Dec 01 '25 01:12 bito-code-review[bot]

/review

X-arshiya-X avatar Dec 01 '25 02:12 X-arshiya-X

Code Review Agent Run #27ee1d

Actionable Suggestions - 1
  • superset/common/query_context_processor.py - 1
    • Blind exception catch without specific type · Line 126-164
Additional Suggestions - 1
  • superset-frontend/src/components/Chart/chartAction.js - 1
    • Parameter naming mismatch · Line 49-56
      The chartUpdateStarted function parameter is named latestQueryId but receives clientId (a client-generated tracking ID), which could confuse developers expecting a query ID. It looks like this might be intentional for tracking, but renaming to clientId would clarify the purpose and avoid potential misuse.
Review Details
  • Files reviewed - 14 · Commit Range: 7506007..4779744
    • superset-frontend/src/components/Chart/chartAction.js
    • superset-frontend/src/components/Chart/chartReducer.ts
    • superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
    • superset-frontend/src/explore/exploreUtils/index.js
    • superset-frontend/src/explore/types.ts
    • superset/charts/schemas.py
    • superset/common/query_context.py
    • superset/common/query_context_factory.py
    • superset/common/query_context_processor.py
    • superset/connectors/sqla/models.py
    • superset/daos/query.py
    • superset/models/core.py
    • superset/models/helpers.py
    • superset/sql_lab.py
  • Files skipped - 0
  • Tools
    • Eslint (Linter) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at [email protected].

Documentation & Help

AI Code Review powered by Bito Logo

bito-code-review[bot] avatar Dec 01 '25 02:12 bito-code-review[bot]

Looks like CI isn't happy at the moment. You'll at least need to run pre-commit, which might clear up some of the other failures too.

Superset uses Git pre-commit hooks courtesy of pre-commit. To install run the following:

pip3 install -r requirements/development.txt
pre-commit install

A series of checks will now run when you make a git commit.

Alternatively it is possible to run pre-commit by running pre-commit manually:

pre-commit run --all-files

rusackas avatar Dec 01 '25 23:12 rusackas

CodeAnt AI is reviewing your PR.

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • [ ] Transaction Behavior
    The new logic that persists the cancel query id calls _db.session.commit() inside _execute_sql_with_mutation_and_logging, which is a low-level helper used for executing SQL. Committing the global session here may unexpectedly commit unrelated pending objects and change transaction boundaries compared to previous behavior. It would be good to confirm this is safe for all call sites and consistent with Superset's transaction management model.

  • [ ] Possible Bug
    In get_df_payload, query_model is only defined inside the if query_obj and cache_key and not cache.is_loaded: block (and within a nested try), but it is later referenced unconditionally in the if query_model is not None: block when building the payload. If the cache is already loaded or an exception is raised before query_model is assigned, this will trigger an UnboundLocalError. The reviewer should verify and fix the initialization/lifetime of query_model so it is always defined before use.

  • [ ] Stability Issue
    In the best-effort creation of a SqlLabQuery row, a broad except Exception around _db.session.commit() only logs the error and sets query_model = None without rolling back the SQLAlchemy session. If the commit fails, the session can remain in a bad state and later metadata operations during the same request may fail. It would be safer to rollback the session and/or narrow the caught exception type.

  • [ ] Possible Bug
    query_model is defined only inside the if query_obj and cache_key and not cache.is_loaded: branch of get_df_payload, but is later referenced unconditionally when constructing the payload. On cache hits (or when query_obj/cache_key is falsy) this can raise an UnboundLocalError when evaluating if query_model is not None:. This control-flow should be adjusted so that query_model is always defined before use or the client-id injection is guarded appropriately.

  • [ ] Performance Issue
    Including client_id in cache_values will likely make the chart data cache effectively per-client, reducing cache hit rates and increasing database load. Since cache_values are typically used to derive the cache key, this change may unintentionally alter caching semantics for all chart queries. It should be validated whether client_id truly needs to participate in the cache key, or if it can be conveyed via a different mechanism that does not fragment the cache.

CodeAnt AI finished reviewing your PR.

CodeAnt AI is running Incremental review