Browse Source

perf(replays): optimize replay count queries (#56358)

add an optimization whereby if an org hasn't sent any replays, we return
early and dont query snuba. adds this under a flagr feature flag so we
can monitor perf during rollout.

relates to https://github.com/getsentry/team-replay/issues/174

---------

Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Josh Ferge 1 year ago
parent
commit
88fe98dd9b

+ 2 - 0
src/sentry/conf/server.py

@@ -1654,6 +1654,8 @@ SENTRY_FEATURES = {
     # Enable core Session Replay SDK for recording on sentry.io
     "organizations:session-replay-sdk": False,
     # Enable core Session Replay SDK for recording onError events on sentry.io
+    "organizations:session-replay-count-query-optimize": False,
+    # Enable core Session Replay SDK for recording onError events on sentry.io
     "organizations:session-replay-sdk-errors-only": False,
     # Enable data scrubbing of replay recording payloads in Relay.
     "organizations:session-replay-recording-scrubbing": False,

+ 1 - 0
src/sentry/features/__init__.py

@@ -188,6 +188,7 @@ default_manager.add("organizations:scim-team-roles", OrganizationFeature, Featur
 default_manager.add("organizations:org-roles-for-teams", OrganizationFeature, FeatureHandlerStrategy.REMOTE)
 default_manager.add("organizations:sentry-functions", OrganizationFeature, FeatureHandlerStrategy.INTERNAL)
 default_manager.add("organizations:session-replay-a11y-tab", OrganizationFeature, FeatureHandlerStrategy.REMOTE)
+default_manager.add("organizations:session-replay-count-query-optimize", OrganizationFeature, FeatureHandlerStrategy.REMOTE)
 default_manager.add("organizations:session-replay-issue-emails", OrganizationFeature, FeatureHandlerStrategy.INTERNAL)
 default_manager.add("organizations:session-replay-recording-scrubbing", OrganizationFeature, FeatureHandlerStrategy.INTERNAL)
 default_manager.add("organizations:session-replay-sdk-errors-only", OrganizationFeature, FeatureHandlerStrategy.REMOTE)

+ 16 - 0
src/sentry/replays/endpoints/organization_replay_count.py

@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+from django.db.models import F
 from rest_framework import serializers, status
 from rest_framework.exceptions import ParseError
 from rest_framework.response import Response
@@ -12,6 +13,7 @@ from sentry.api.bases import NoProjects
 from sentry.api.bases.organization_events import OrganizationEventsV2EndpointBase
 from sentry.exceptions import InvalidSearchQuery
 from sentry.models.organization import Organization
+from sentry.models.project import Project
 from sentry.replays.usecases.replay_counts import get_replay_counts
 from sentry.snuba.dataset import Dataset
 from sentry.types.ratelimit import RateLimit, RateLimitCategory
@@ -63,6 +65,12 @@ class OrganizationReplayCountEndpoint(OrganizationEventsV2EndpointBase):
         except NoProjects:
             return Response({})
 
+        if features.has(
+            "organizations:session-replay-count-query-optimize", organization, actor=request.user
+        ):
+            if not project_in_org_has_sent_replay(organization):
+                return Response({})
+
         result = ReplayDataSourceValidator(data=request.GET)
         if not result.is_valid():
             raise ParseError(result.errors)
@@ -77,3 +85,11 @@ class OrganizationReplayCountEndpoint(OrganizationEventsV2EndpointBase):
             return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
 
         return self.respond(replay_counts)
+
+
+def project_in_org_has_sent_replay(organization):
+    return (
+        Project.objects.filter(organization=organization)
+        .filter(flags=F("flags").bitor(Project.flags.has_replays))
+        .exists()
+    )

+ 30 - 0
tests/sentry/replays/test_organization_replay_count.py

@@ -5,8 +5,11 @@ import uuid
 from typing import Any
 
 import pytest
+from django.db.models import F
 from django.urls import reverse
 
+from sentry.models.project import Project
+from sentry.replays.endpoints.organization_replay_count import project_in_org_has_sent_replay
 from sentry.replays.testutils import mock_replay
 from sentry.snuba.dataset import Dataset
 from sentry.testutils.cases import (
@@ -512,3 +515,30 @@ class OrganizationReplayCountEndpointTest(
             b'{"detail":"Invalid quote at \'[\\"root\': quotes must enclose text or be '
             b'escaped."}'
         ), response.content
+
+    def test_endpoint_org_hasnt_sent_replays(self):
+        event_id_a = "a" * 32
+        event_a = self.store_event(
+            data={
+                "event_id": event_id_a,
+                "timestamp": iso_format(self.min_ago),
+                "fingerprint": ["group-1"],
+            },
+            project_id=self.project.id,
+        )
+        query = {"query": f"issue.id:[{event_a.group.id}]"}
+
+        with self.feature(self.features):
+            response = self.client.get(self.url, query, format="json")
+
+        assert response.status_code == 200, response.content
+        assert response.data == {}
+
+    def test_project_in_org_has_sent_replay(self):
+        org = self.create_organization()
+        project = self.create_project(organization=org)
+        assert project_in_org_has_sent_replay(org) is False
+
+        project.update(flags=F("flags").bitor(Project.flags.has_replays))
+
+        assert project_in_org_has_sent_replay(org) is True