Skip to content

Commit b851b90

Browse files
authored
fix(stats): Disable timestamp from top events (#92547)
- This is so we throw an error if users try to use timestamp as a groupby in timeseries requests
1 parent 07f27ea commit b851b90

File tree

5 files changed

+48
-74
lines changed

5 files changed

+48
-74
lines changed

src/sentry/api/endpoints/organization_events_stats.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import Any
44

55
import sentry_sdk
6-
from rest_framework.exceptions import ValidationError
6+
from rest_framework.exceptions import ParseError, ValidationError
77
from rest_framework.request import Request
88
from rest_framework.response import Response
99

@@ -223,12 +223,15 @@ def _get_event_stats(
223223
final_columns = transform_query_columns_for_error_upsampling(query_columns)
224224

225225
if top_events > 0:
226+
raw_groupby = self.get_field_list(organization, request)
227+
if "timestamp" in raw_groupby:
228+
raise ParseError("Cannot group by timestamp")
226229
if use_rpc:
227230
return scoped_dataset.run_top_events_timeseries_query(
228231
params=snuba_params,
229232
query_string=query,
230233
y_axes=final_columns,
231-
raw_groupby=self.get_field_list(organization, request),
234+
raw_groupby=raw_groupby,
232235
orderby=self.get_orderby(request),
233236
limit=top_events,
234237
referrer=referrer,
@@ -243,7 +246,7 @@ def _get_event_stats(
243246
)
244247
return scoped_dataset.top_events_timeseries(
245248
timeseries_columns=final_columns,
246-
selected_columns=self.get_field_list(organization, request),
249+
selected_columns=raw_groupby,
247250
equations=self.get_equation_list(organization, request),
248251
user_query=query,
249252
snuba_params=snuba_params,

src/sentry/api/endpoints/organization_events_timeseries.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,12 +234,15 @@ def get_event_stats(
234234
)
235235

236236
if top_events > 0:
237+
raw_groupby = self.get_field_list(organization, request, param_name="groupBy")
238+
if "timestamp" in raw_groupby:
239+
raise ParseError("Cannot group by timestamp")
237240
if dataset in {spans_rpc, ourlogs}:
238241
return dataset.run_top_events_timeseries_query(
239242
params=snuba_params,
240243
query_string=query,
241244
y_axes=query_columns,
242-
raw_groupby=self.get_field_list(organization, request, param_name="groupBy"),
245+
raw_groupby=raw_groupby,
243246
orderby=self.get_orderby(request),
244247
limit=top_events,
245248
referrer=referrer,
@@ -254,7 +257,7 @@ def get_event_stats(
254257
)
255258
return dataset.top_events_timeseries(
256259
timeseries_columns=query_columns,
257-
selected_columns=self.get_field_list(organization, request, param_name="groupBy"),
260+
selected_columns=raw_groupby,
258261
equations=self.get_equation_list(organization, request),
259262
user_query=query,
260263
snuba_params=snuba_params,

tests/snuba/api/endpoints/test_organization_events_stats.py

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2254,41 +2254,6 @@ def test_top_events_with_error_unhandled(self):
22542254
assert response.status_code == 200, response.content
22552255
assert len(data) == 2
22562256

2257-
def test_top_events_with_timestamp(self):
2258-
with self.feature(self.enabled_features):
2259-
response = self.client.get(
2260-
self.url,
2261-
data={
2262-
"start": self.day_ago.isoformat(),
2263-
"end": (self.day_ago + timedelta(hours=2)).isoformat(),
2264-
"interval": "1h",
2265-
"yAxis": "count()",
2266-
"orderby": ["-count()"],
2267-
"query": "event.type:default",
2268-
"field": ["count()", "message", "timestamp"],
2269-
"topEvents": "5",
2270-
},
2271-
format="json",
2272-
)
2273-
2274-
data = response.data
2275-
assert response.status_code == 200, response.content
2276-
assert len(data) == 6
2277-
# Transactions won't be in the results because of the query
2278-
del self.events[4]
2279-
del self.event_data[4]
2280-
2281-
for index, event in enumerate(self.events[:5]):
2282-
results = data[",".join([event.message, event.timestamp])]
2283-
assert results["order"] == index
2284-
assert [{"count": self.event_data[index]["count"]}] in [
2285-
attrs for time, attrs in results["data"]
2286-
]
2287-
2288-
other = data["Other"]
2289-
assert other["order"] == 5
2290-
assert [{"count": 1}] in [attrs for _, attrs in other["data"]]
2291-
22922257
def test_top_events_with_int(self):
22932258
with self.feature(self.enabled_features):
22942259
response = self.client.get(
@@ -2715,40 +2680,6 @@ def test_invalid_interval(self, mock_raw_query, mock_bulk_query):
27152680
# Should've default to 24h's default of 5m
27162681
assert mock_raw_query.mock_calls[5].args[0].query.granularity.granularity == 300
27172682

2718-
def test_top_events_timestamp_fields(self):
2719-
with self.feature(self.enabled_features):
2720-
response = self.client.get(
2721-
self.url,
2722-
format="json",
2723-
data={
2724-
"start": self.day_ago.isoformat(),
2725-
"end": (self.day_ago + timedelta(hours=2)).isoformat(),
2726-
"interval": "1h",
2727-
"yAxis": "count()",
2728-
"orderby": ["-count()"],
2729-
"field": ["count()", "timestamp", "timestamp.to_hour", "timestamp.to_day"],
2730-
"topEvents": "5",
2731-
},
2732-
)
2733-
assert response.status_code == 200
2734-
data = response.data
2735-
assert len(data) == 3
2736-
2737-
# these are the timestamps corresponding to the events stored
2738-
timestamps = [
2739-
self.day_ago + timedelta(minutes=2),
2740-
self.day_ago + timedelta(hours=1, minutes=2),
2741-
self.day_ago + timedelta(minutes=4),
2742-
]
2743-
timestamp_hours = [timestamp.replace(minute=0, second=0) for timestamp in timestamps]
2744-
timestamp_days = [timestamp.replace(hour=0, minute=0, second=0) for timestamp in timestamps]
2745-
2746-
for ts, ts_hr, ts_day in zip(timestamps, timestamp_hours, timestamp_days):
2747-
key = f"{ts.isoformat()},{ts_day.isoformat()},{ts_hr.isoformat()}"
2748-
count = sum(e["count"] for e in self.event_data if e["data"]["timestamp"] == ts)
2749-
results = data[key]
2750-
assert [{"count": count}] in [attrs for time, attrs in results["data"]]
2751-
27522683
def test_top_events_other_with_matching_columns(self):
27532684
with self.feature(self.enabled_features):
27542685
response = self.client.get(

tests/snuba/api/endpoints/test_organization_events_stats_span_indexed.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,24 @@ def test_datetime_unaligned_with_regular_buckets(self):
21932193
# The timestamp of the last event should be 22:00 and there should also only be 1 event
21942194
assert data[-1] == ((self.day_ago + timedelta(hours=12)).timestamp(), [{"count": 1}])
21952195

2196+
def test_top_events_with_timestamp(self):
2197+
"""Users shouldn't groupby timestamp for top events"""
2198+
response = self._do_request(
2199+
data={
2200+
"start": self.day_ago,
2201+
"end": self.day_ago + timedelta(minutes=6),
2202+
"interval": "1m",
2203+
"yAxis": "count()",
2204+
"field": ["timestamp", "sum(span.self_time)"],
2205+
"orderby": ["-sum_span_self_time"],
2206+
"project": self.project.id,
2207+
"dataset": self.dataset,
2208+
"excludeOther": 0,
2209+
"topEvents": 2,
2210+
},
2211+
)
2212+
assert response.status_code == 400, response.content
2213+
21962214
def test_simple_equation(self):
21972215
self.store_spans(
21982216
[

tests/snuba/api/endpoints/test_organization_events_timeseries_spans.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2101,3 +2101,22 @@ def test_disable_extrapolation(self):
21012101
"valueType": "integer",
21022102
"interval": 3_600_000,
21032103
}
2104+
2105+
def test_top_events_with_timestamp(self):
2106+
"""Users shouldn't groupby timestamp for top events"""
2107+
response = self._do_request(
2108+
data={
2109+
"start": self.start,
2110+
"end": self.end,
2111+
"interval": "1m",
2112+
"yAxis": "count(span.self_time)",
2113+
"groupBy": ["timestamp", "count(span.self_time)"],
2114+
"query": "count(span.self_time):>4",
2115+
"orderby": ["-count(span.self_time)"],
2116+
"project": self.project.id,
2117+
"dataset": "spans",
2118+
"excludeOther": 0,
2119+
"topEvents": 5,
2120+
},
2121+
)
2122+
assert response.status_code == 400, response.content

0 commit comments

Comments
 (0)