Browse Source

feat(grouping): return project events in pseudo-random order (#68236)

Return project's events in pseudo-random order. This used ordering by
event.id, which is an UUID, thus should be more or less in pseudo-random
order.
Bartek Ogryczak 11 months ago
parent
commit
d026cd635a

+ 10 - 0
src/sentry/api/endpoints/project_events.py

@@ -11,6 +11,7 @@ from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import region_silo_endpoint
 from sentry.api.bases.project import ProjectEndpoint
 from sentry.api.serializers import EventSerializer, SimpleEventSerializer, serialize
+from sentry.snuba.events import Columns
 from sentry.types.ratelimit import RateLimit, RateLimitCategory
 
 
@@ -42,6 +43,9 @@ class ProjectEventsEndpoint(ProjectEndpoint):
                            include the full event body, including the stacktrace.
                            Set to 1 to enable.
 
+        :qparam bool sample: return events in pseudo-random order. This is deterministic,
+                             same query will return the same events in the same order.
+
         :pparam string organization_slug: the slug of the organization the
                                           groups belong to.
         :pparam string project_slug: the slug of the project the groups
@@ -61,6 +65,7 @@ class ProjectEventsEndpoint(ProjectEndpoint):
             event_filter.start = timezone.now() - timedelta(days=7)
 
         full = request.GET.get("full", False)
+        sample = request.GET.get("sample", False)
 
         data_fn = partial(
             eventstore.backend.get_events,
@@ -69,6 +74,11 @@ class ProjectEventsEndpoint(ProjectEndpoint):
             tenant_ids={"organization_id": project.organization_id},
         )
 
+        if sample:
+            # not a true random ordering, but event_id is UUID, that's random enough
+            # for our purposes and doesn't have heavy performance impact
+            data_fn = partial(data_fn, orderby=[Columns.EVENT_ID.value.alias])
+
         serializer = EventSerializer() if full else SimpleEventSerializer()
         return self.paginate(
             request=request,

+ 26 - 0
tests/snuba/api/endpoints/test_project_events.py

@@ -97,3 +97,29 @@ class ProjectEventsTest(APITestCase, SnubaTestCase):
             assert response.status_code == 200, response.content
             assert len(response.data) == 1
             assert response.data[0]["eventID"] == event.event_id
+
+    def test_sample(self):
+        self.login_as(user=self.user)
+
+        project = self.create_project()
+        event_1 = self.store_event(
+            data={"timestamp": iso_format(before_now(minutes=1))}, project_id=project.id
+        )
+        event_2 = self.store_event(
+            data={"timestamp": iso_format(before_now(minutes=1))}, project_id=project.id
+        )
+
+        url = reverse(
+            "sentry-api-0-project-events",
+            kwargs={
+                "organization_slug": project.organization.slug,
+                "project_slug": project.slug,
+            },
+        )
+        response = self.client.get(url, {"sample": "true"}, format="json")
+
+        assert response.status_code == 200, response.content
+        assert len(response.data) == 2
+        assert [event["eventID"] for event in response.data] == sorted(
+            [event_1.event_id, event_2.event_id]
+        )