This is an automated email from the ASF dual-hosted git repository.
msyavuz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 7b21979fa3b fix(charts): Force refresh uses async mode when GAQ is
enabled (#37845)
7b21979fa3b is described below
commit 7b21979fa3b53b769b7c6ce5ba364fae4b6bfcea
Author: Mehmet Salih Yavuz <[email protected]>
AuthorDate: Mon Feb 16 21:45:10 2026 +0300
fix(charts): Force refresh uses async mode when GAQ is enabled (#37845)
---
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):