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

msyavuz pushed a commit to branch msyavuz/fix/refresh-async
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 0bab66ed5e55c00fd19734763cb06e76c7ecab34
Author: Mehmet Salih Yavuz <[email protected]>
AuthorDate: Tue Feb 10 18:57:42 2026 +0300

    fix(charts): Force refresh skips cache and triggers async job
---
 superset/charts/data/api.py                      | 20 ++++++-----
 tests/integration_tests/charts/data/api_tests.py | 42 ++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 9 deletions(-)

diff --git a/superset/charts/data/api.py b/superset/charts/data/api.py
index 6166876a36b..cfa84525d73 100644
--- a/superset/charts/data/api.py
+++ b/superset/charts/data/api.py
@@ -351,15 +351,17 @@ class ChartDataRestApi(ChartRestApi):
         """
         Execute command as an async query.
         """
-        # First, look for the chart query results in the cache.
-        with contextlib.suppress(ChartDataCacheLoadError):
-            result = command.run(force_cached=True)
-            if result is not None:
-                # Log is_cached if extra payload callback is provided.
-                # This indicates no async job was triggered - data was already 
cached
-                # and a synchronous response is being returned immediately.
-                self._log_is_cached(result, add_extra_log_payload)
-                return self._send_chart_response(result)
+        # First, look for the chart query results in the cache,
+        # but only if we're not forcing a refresh.
+        if not form_data.get("force"):
+            with contextlib.suppress(ChartDataCacheLoadError):
+                result = command.run(force_cached=True)
+                if result is not None:
+                    # Log is_cached if extra payload callback is provided.
+                    # This indicates no async job was triggered - data was 
already
+                    # cached and a synchronous response is being returned 
immediately.
+                    self._log_is_cached(result, add_extra_log_payload)
+                    return self._send_chart_response(result)
         # Otherwise, kick off a background job to run the chart query.
         # Clients will either poll or be notified of query completion,
         # at which point they will call the /data/<cache_key> endpoint
diff --git a/tests/integration_tests/charts/data/api_tests.py 
b/tests/integration_tests/charts/data/api_tests.py
index 39241cc47f8..a4c1b95c9e6 100644
--- a/tests/integration_tests/charts/data/api_tests.py
+++ b/tests/integration_tests/charts/data/api_tests.py
@@ -832,6 +832,48 @@ class TestPostChartDataApi(BaseTestChartDataApi):
         # is_cached should be [True] when retrieved from cache
         assert records[0]["is_cached"] == [True]
 
+    @with_feature_flags(GLOBAL_ASYNC_QUERIES=True)
+    @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
+    @mock.patch("superset.charts.data.api.ChartDataCommand.run")
+    def test_chart_data_async_force_refresh(self, mock_run):
+        """
+        Chart data API: Test that force=true skips cache and triggers async job
+        """
+        app._got_first_request = False
+        async_query_manager_factory.init_app(app)
+
+        # Mock the command.run to return cached data
+        class QueryContext:
+            result_format = ChartDataResultFormat.JSON
+            result_type = ChartDataResultType.FULL
+
+        mock_run.return_value = {
+            "query_context": QueryContext(),
+            "queries": [{"query": "select * from foo", "is_cached": True}],
+        }
+
+        # Test without force - should return cached data synchronously
+        self.query_context_payload["result_type"] = ChartDataResultType.FULL
+        rv = self.post_assert_metric(CHART_DATA_URI, 
self.query_context_payload, "data")
+        assert rv.status_code == 200
+        mock_run.assert_called_once_with(force_cached=True)
+
+        # Reset the mock
+        mock_run.reset_mock()
+
+        # Test with force=true - should skip cache and return async response
+        self.query_context_payload["force"] = True
+        rv = self.post_assert_metric(CHART_DATA_URI, 
self.query_context_payload, "data")
+        assert rv.status_code == 202
+        # When force=true, command.run should not be called at all in 
_run_async
+        # since we skip the cache check entirely
+        mock_run.assert_not_called()
+        data = json.loads(rv.data.decode("utf-8"))
+        keys = list(data.keys())
+        self.assertCountEqual(  # noqa: PT009
+            keys, ["channel_id", "job_id", "user_id", "status", "errors", 
"result_url"]
+        )
+
     @with_feature_flags(GLOBAL_ASYNC_QUERIES=True)
     @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
     def test_chart_data_async_results_type(self):

Reply via email to