From 3a35ee0375f59fd831973a18cfd1159a3931f192 Mon Sep 17 00:00:00 2001 From: William Mak Date: Mon, 25 May 2026 17:22:11 -0400 Subject: [PATCH 1/4] fix(events): Debug param wasn't being passed down correctly - on tables the param wasn't being passed to the table_rpc - on timeseries the debug wasn't being included in the response meta --- .../organization_events_timeseries.py | 19 ++++++--- src/sentry/api/endpoints/timeseries.py | 1 + src/sentry/snuba/rpc_dataset_common.py | 6 ++- .../api/endpoints/test_organization_events.py | 2 +- ...st_organization_events_timeseries_spans.py | 42 ++++++++++++++++++- 5 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/sentry/api/endpoints/organization_events_timeseries.py b/src/sentry/api/endpoints/organization_events_timeseries.py index 485b7711f483f4..bd75846b25ea1e 100644 --- a/src/sentry/api/endpoints/organization_events_timeseries.py +++ b/src/sentry/api/endpoints/organization_events_timeseries.py @@ -366,12 +366,21 @@ def serialize_stats_data( ) -> StatsResponse: # We need the current timestamp for the Ingestion Delay incomplete reason now = datetime.now().timestamp() + stats_meta = StatsMeta( + dataset=DATASET_LABELS[dataset], + start=snuba_params.start_date.timestamp() * 1000, + end=snuba_params.end_date.timestamp() * 1000, + ) + if snuba_params.debug: + if isinstance(result, SnubaTSResult) and "debug_info" in result.data["meta"]: + stats_meta["debug_info"] = result.data["meta"]["debug_info"] + elif isinstance(result, dict): + stats_meta["debug_info"] = {} + for key, keyed_result in result.items(): + if "debug_info" in keyed_result.data["meta"]: + stats_meta["debug_info"][key] = keyed_result.data["meta"]["debug_info"] response = StatsResponse( - meta=StatsMeta( - dataset=DATASET_LABELS[dataset], - start=snuba_params.start_date.timestamp() * 1000, - end=snuba_params.end_date.timestamp() * 1000, - ), + meta=stats_meta, timeSeries=self.serialize_result(result, axes, rollup, now), ) return response diff --git a/src/sentry/api/endpoints/timeseries.py b/src/sentry/api/endpoints/timeseries.py index 20aeae732171f6..5e76db2f25990f 100644 --- a/src/sentry/api/endpoints/timeseries.py +++ b/src/sentry/api/endpoints/timeseries.py @@ -9,6 +9,7 @@ class StatsMeta(TypedDict): dataset: str start: float end: float + debug_info: NotRequired[Any] class Row(TypedDict): diff --git a/src/sentry/snuba/rpc_dataset_common.py b/src/sentry/snuba/rpc_dataset_common.py index 37bcdc4dade70d..582f3f9bf0fe4f 100644 --- a/src/sentry/snuba/rpc_dataset_common.py +++ b/src/sentry/snuba/rpc_dataset_common.py @@ -392,7 +392,7 @@ def _run_table_query( table_request = cls.get_table_rpc_request(query) rpc_request = table_request.rpc_request try: - rpc_response = snuba_rpc.table_rpc([rpc_request])[0] + rpc_response = snuba_rpc.table_rpc([rpc_request], debug=debug)[0] except Exception as e: # add the rpc to the error so we can include it in the response if debug: @@ -428,7 +428,9 @@ def run_table_query( @classmethod @sentry_sdk.trace - def run_bulk_table_queries(cls, queries: list[TableQuery]) -> dict[str, EAPResponse]: + def run_bulk_table_queries( + cls, queries: list[TableQuery], debug: str | bool = False + ) -> dict[str, EAPResponse]: """Validate the bulk queries""" names: set[str] = set() for query in queries: diff --git a/tests/snuba/api/endpoints/test_organization_events.py b/tests/snuba/api/endpoints/test_organization_events.py index bf521f0b6342b0..d51b4590bc88ba 100644 --- a/tests/snuba/api/endpoints/test_organization_events.py +++ b/tests/snuba/api/endpoints/test_organization_events.py @@ -6021,7 +6021,7 @@ def test_debug_param(self) -> None: ) assert response.status_code == 200, response.content # Debug should be ignored without superuser - assert "query" not in response.data["meta"] + assert "debug_info" not in response.data["meta"] self.user = self.create_user("superuser@example.com", is_superuser=True) self.create_team(organization=self.organization, members=[self.user]) diff --git a/tests/snuba/api/endpoints/test_organization_events_timeseries_spans.py b/tests/snuba/api/endpoints/test_organization_events_timeseries_spans.py index 056afdb5ec04a1..fe87838eea7ce7 100644 --- a/tests/snuba/api/endpoints/test_organization_events_timeseries_spans.py +++ b/tests/snuba/api/endpoints/test_organization_events_timeseries_spans.py @@ -103,7 +103,6 @@ def test_count(self) -> None: ], ) self.store_spans(spans) - self.user = self.create_user("superuser@example.com", is_superuser=True) response = self._do_request( data={ @@ -113,7 +112,6 @@ def test_count(self) -> None: "yAxis": "count()", "project": self.project.id, "dataset": "spans", - "debug": "wmak", }, ) assert response.status_code == 200, response.content @@ -141,6 +139,46 @@ def test_count(self) -> None: "interval": 3_600_000, } + def test_debug_param(self) -> None: + self.user = self.create_user("user@example.com", is_superuser=False) + self.create_team(organization=self.organization, members=[self.user]) + self.login_as(user=self.user) + + response = self._do_request( + { + "start": self.start, + "end": self.end, + "interval": "1h", + "yAxis": "count()", + "project": self.project.id, + "dataset": "spans", + "debug": True, + }, + ) + assert response.status_code == 200, response.content + # Debug should be ignored without superuser + assert "debug_info" not in response.data["meta"] + + self.user = self.create_user("superuser@example.com", is_superuser=True) + self.create_team(organization=self.organization, members=[self.user]) + self.login_as(user=self.user) + + response = self._do_request( + { + "start": self.start, + "end": self.end, + "interval": "1h", + "yAxis": "count()", + "project": self.project.id, + "dataset": "spans", + "debug": True, + }, + ) + assert response.status_code == 200, response.content + assert "debug_info" in response.data["meta"] + # We should get the query back in the query key + assert "expressions" in response.data["meta"]["debug_info"]["query"] + def test_handle_nans_from_snuba(self) -> None: self.store_spans( [self.create_span({"description": "foo"}, start_ts=self.start)], From 81ec424c3b98b04bd0df2ae502eb8d0d896fc42e Mon Sep 17 00:00:00 2001 From: William Mak Date: Tue, 26 May 2026 12:51:55 -0400 Subject: [PATCH 2/4] fix: remove debug_Info from type --- src/sentry/api/endpoints/timeseries.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sentry/api/endpoints/timeseries.py b/src/sentry/api/endpoints/timeseries.py index 5e76db2f25990f..20aeae732171f6 100644 --- a/src/sentry/api/endpoints/timeseries.py +++ b/src/sentry/api/endpoints/timeseries.py @@ -9,7 +9,6 @@ class StatsMeta(TypedDict): dataset: str start: float end: float - debug_info: NotRequired[Any] class Row(TypedDict): From cf1743dd84fe425e1c059ca55b30fe46d1af2735 Mon Sep 17 00:00:00 2001 From: William Mak Date: Tue, 26 May 2026 12:59:52 -0400 Subject: [PATCH 3/4] fix: remove debug_info from openapi --- .../api/endpoints/organization_events_timeseries.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sentry/api/endpoints/organization_events_timeseries.py b/src/sentry/api/endpoints/organization_events_timeseries.py index bd75846b25ea1e..f4423732a81a20 100644 --- a/src/sentry/api/endpoints/organization_events_timeseries.py +++ b/src/sentry/api/endpoints/organization_events_timeseries.py @@ -373,12 +373,14 @@ def serialize_stats_data( ) if snuba_params.debug: if isinstance(result, SnubaTSResult) and "debug_info" in result.data["meta"]: - stats_meta["debug_info"] = result.data["meta"]["debug_info"] + debug_info = result.data["meta"]["debug_info"] elif isinstance(result, dict): - stats_meta["debug_info"] = {} + debug_info = {} for key, keyed_result in result.items(): if "debug_info" in keyed_result.data["meta"]: - stats_meta["debug_info"][key] = keyed_result.data["meta"]["debug_info"] + debug_info[key] = keyed_result.data["meta"]["debug_info"] + # ignore typing here cause we don't want the openapi docs to include debug_info + stats_meta["debug_info"] = debug_info # type: ignore[typeddict-unknown-key] response = StatsResponse( meta=stats_meta, timeSeries=self.serialize_result(result, axes, rollup, now), From f5846b5192d6712233dc4d15dfc40c0d86170730 Mon Sep 17 00:00:00 2001 From: William Mak Date: Tue, 26 May 2026 13:19:12 -0400 Subject: [PATCH 4/4] fix ai nit --- src/sentry/api/endpoints/organization_events_timeseries.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/api/endpoints/organization_events_timeseries.py b/src/sentry/api/endpoints/organization_events_timeseries.py index f4423732a81a20..4e64cdcb70494f 100644 --- a/src/sentry/api/endpoints/organization_events_timeseries.py +++ b/src/sentry/api/endpoints/organization_events_timeseries.py @@ -372,6 +372,7 @@ def serialize_stats_data( end=snuba_params.end_date.timestamp() * 1000, ) if snuba_params.debug: + debug_info = None if isinstance(result, SnubaTSResult) and "debug_info" in result.data["meta"]: debug_info = result.data["meta"]["debug_info"] elif isinstance(result, dict):