Browse Source

chore(performance): Remove old anomaly detection backend (#80696)

Effectively reverts https://github.com/getsentry/sentry/pull/31533/. As
far as we can tell, this never shipped, and has been replaced with a
different service. You tell me, though!

Frontend removed in https://github.com/getsentry/sentry/pull/80692
George Gritsouk 3 months ago
parent
commit
7e3f4c3d26

+ 0 - 2
.github/CODEOWNERS

@@ -228,8 +228,6 @@ yarn.lock                                                @getsentry/owners-js-de
 /tests/snuba/api/endpoints/test_organization_events_vitals.py               @getsentry/visibility
 /tests/snuba/api/endpoints/test_organization_tagkey_values.py               @getsentry/visibility
 
-/src/sentry/api/endpoints/organization_transaction_anomaly_detection.py     @getsentry/data
-
 /src/sentry/spans/                                                          @getsentry/visibility
 /tests/sentry/spans/                                                        @getsentry/visibility
 

+ 0 - 143
src/sentry/api/endpoints/organization_transaction_anomaly_detection.py

@@ -1,143 +0,0 @@
-from collections import namedtuple
-from datetime import datetime, timedelta, timezone
-
-import orjson
-from django.conf import settings
-from rest_framework.request import Request
-from rest_framework.response import Response
-from urllib3 import Retry
-
-from sentry import features
-from sentry.api.api_publish_status import ApiPublishStatus
-from sentry.api.base import region_silo_endpoint
-from sentry.api.bases import OrganizationEventsEndpointBase
-from sentry.api.utils import get_date_range_from_params, handle_query_errors
-from sentry.net.http import connection_from_url
-from sentry.snuba.metrics_enhanced_performance import timeseries_query
-
-ads_connection_pool = connection_from_url(
-    settings.SEER_ANOMALY_DETECTION_URL,
-    retries=Retry(
-        total=5,
-        status_forcelist=[408, 429, 502, 503, 504],
-    ),
-    timeout=settings.SEER_ANOMALY_DETECTION_TIMEOUT,
-)
-
-MappedParams = namedtuple("MappedParams", ["query_start", "query_end", "granularity"])
-
-
-def get_anomalies(snuba_io):
-    response = ads_connection_pool.urlopen(
-        "POST",
-        "/anomaly/predict",
-        body=orjson.dumps(snuba_io, option=orjson.OPT_UTC_Z),
-        headers={"content-type": "application/json;charset=utf-8"},
-    )
-    return Response(orjson.loads(response.data), status=200)
-
-
-def get_time_params(start: datetime, end: datetime) -> MappedParams:
-    """
-    Takes visualization start/end timestamps
-    and returns the start/end/granularity
-    of the snuba query that we should execute
-    Attributes:
-    start: datetime representing start of visualization window
-    end: datetime representing end of visualization window
-    Returns:
-    results: namedtuple containing
-        query_start: datetime representing start of query window
-        query_end: datetime representing end of query window
-        granularity: granularity to use (in seconds)
-    """
-    anomaly_detection_range = end - start
-
-    if anomaly_detection_range > timedelta(days=14):
-        snuba_range = timedelta(days=90)
-        granularity = 3600
-
-    elif anomaly_detection_range > timedelta(days=1):
-        granularity = 1200
-        snuba_range = timedelta(days=28)
-
-    else:
-        snuba_range = timedelta(days=14)
-        granularity = 600
-
-    additional_time_needed = snuba_range - anomaly_detection_range
-    now = datetime.now(timezone.utc)
-    start_limit = now - timedelta(days=90)
-    end_limit = now
-    start = max(start, start_limit)
-    end = min(end, end_limit)
-    # By default, expand windows equally in both directions
-    window_increase = additional_time_needed / 2
-    query_start, query_end = None, None
-
-    # If window will go back farther than 90 days, use today - 90 as start
-    if start - window_increase < start_limit:
-        query_start = now - timedelta(days=90)
-        additional_time_needed -= start - query_start
-        window_increase = additional_time_needed
-    # If window extends beyond today, use today as end
-    if end + window_increase > end_limit:
-        query_end = now
-        additional_time_needed -= query_end - end
-        window_increase = additional_time_needed
-
-    query_start = query_start or max(start - window_increase, start_limit)
-    query_end = query_end or min(end + window_increase, end_limit)
-
-    return MappedParams(
-        query_start,
-        query_end,
-        granularity,
-    )
-
-
-@region_silo_endpoint
-class OrganizationTransactionAnomalyDetectionEndpoint(OrganizationEventsEndpointBase):
-    publish_status = {
-        "GET": ApiPublishStatus.PRIVATE,
-    }
-
-    def has_feature(self, organization, request):
-        return features.has(
-            "organizations:performance-anomaly-detection-ui", organization, actor=request.user
-        )
-
-    def get(self, request: Request, organization) -> Response:
-        if not self.has_feature(organization, request):
-            return Response(status=404)
-
-        start, end = get_date_range_from_params(request.GET)
-        time_params = get_time_params(start, end)
-        snuba_params = self.get_snuba_params(request, organization)
-        query = request.GET.get("query")
-        query = f"{query} event.type:transaction" if query else "event.type:transaction"
-
-        datetime_format = "%Y-%m-%d %H:%M:%S"
-        ads_request = {
-            "query": query,
-            "start": start.strftime(datetime_format),
-            "end": end.strftime(datetime_format),
-            "granularity": time_params.granularity,
-        }
-
-        # overwrite relevant time params
-        snuba_params.start = time_params.query_start
-        snuba_params.end = time_params.query_end
-
-        with handle_query_errors():
-            snuba_response = timeseries_query(
-                selected_columns=["count()"],
-                query=query,
-                snuba_params=snuba_params,
-                rollup=time_params.granularity,
-                referrer="transaction-anomaly-detection",
-                zerofill_results=False,
-            )
-            ads_request["data"] = snuba_response.data["data"]
-
-            return get_anomalies(ads_request)

+ 0 - 8
src/sentry/api/urls.py

@@ -560,9 +560,6 @@ from .endpoints.organization_traces import (
     OrganizationTraceSpansEndpoint,
     OrganizationTracesStatsEndpoint,
 )
-from .endpoints.organization_transaction_anomaly_detection import (
-    OrganizationTransactionAnomalyDetectionEndpoint,
-)
 from .endpoints.organization_user_details import OrganizationUserDetailsEndpoint
 from .endpoints.organization_user_reports import OrganizationUserReportsEndpoint
 from .endpoints.organization_user_teams import OrganizationUserTeamsEndpoint
@@ -1997,11 +1994,6 @@ ORGANIZATION_URLS = [
         OrganizationJoinRequestEndpoint.as_view(),
         name="sentry-api-0-organization-join-request",
     ),
-    re_path(
-        r"^(?P<organization_id_or_slug>[^\/]+)/transaction-anomaly-detection/$",
-        OrganizationTransactionAnomalyDetectionEndpoint.as_view(),
-        name="sentry-api-0-organization-transaction-anomaly-detection",
-    ),
     # relay usage
     re_path(
         r"^(?P<organization_id_or_slug>[^\/]+)/relay_usage/$",

+ 0 - 2
src/sentry/features/temporary.py

@@ -242,8 +242,6 @@ def register_temporary_features(manager: FeatureManager):
     manager.add("organizations:ownership-size-limit-large", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=False)
     # Enable xlarge ownership rule file size limit
     manager.add("organizations:ownership-size-limit-xlarge", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=False)
-    # Enable views for anomaly detection
-    manager.add("organizations:performance-anomaly-detection-ui", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
     # Enable mobile performance score calculation for transactions in relay
     manager.add("organizations:performance-calculate-mobile-perf-score-relay", OrganizationFeature, FeatureHandlerStrategy.INTERNAL, api_expose=False)
     # Enable performance change explorer panel on trends page

+ 0 - 163
tests/sentry/api/endpoints/test_organization_transaction_anomaly_detection.py

@@ -1,163 +0,0 @@
-from collections import namedtuple
-from datetime import datetime, timezone
-from unittest import mock
-
-from django.http import HttpResponse
-from django.urls import reverse
-
-from sentry.api.endpoints.organization_transaction_anomaly_detection import get_time_params
-from sentry.testutils.cases import APITestCase, SnubaTestCase
-from sentry.testutils.helpers.datetime import freeze_time
-
-
-@freeze_time("2022-02-21")
-class OrganizationTransactionAnomalyDetectionEndpointTest(APITestCase, SnubaTestCase):
-    endpoint = "sentry-api-0-organization-transaction-anomaly-detection"
-
-    def setUp(self):
-        super().setUp()
-        self.project = self.create_project(organization=self.organization)
-        self.login_as(user=self.user)
-        self.features = {}
-        self.snuba_raw_data = [(0, 0), (3, 0), (0, 0)]
-
-    def do_request(self, data, url=None, features=None):
-        self.url = reverse(
-            "sentry-api-0-organization-transaction-anomaly-detection",
-            kwargs={"organization_id_or_slug": self.project.organization.slug},
-        )
-
-        if features is None:
-            features = {
-                "organizations:discover-basic": True,
-                "organizations:performance-anomaly-detection-ui": True,
-            }
-        features.update(self.features)
-        with self.feature(features):
-            return self.client.get(self.url if url is None else url, data=data, format="json")
-
-    def test_without_feature(self):
-        self.url = reverse(
-            "sentry-api-0-organization-transaction-anomaly-detection",
-            kwargs={"organization_id_or_slug": self.project.organization.slug},
-        )
-
-        response = self.client.get(self.url, data={}, format="json")
-        self.assertEqual(response.status_code, 404)
-
-    @mock.patch("sentry.api.endpoints.organization_transaction_anomaly_detection.timeseries_query")
-    @mock.patch("sentry.api.endpoints.organization_transaction_anomaly_detection.get_anomalies")
-    def test_get_start_end(self, mock_get_anomalies, mock_timeseries_query):
-        SnubaTSResult = namedtuple("SnubaTSResult", "data")
-        mock_timeseries_query.return_value = SnubaTSResult(
-            data={"data": self.snuba_raw_data},
-        )
-        mock_get_anomalies.return_value = HttpResponse({"key": "value"})
-        request = {
-            "project": self.project.id,
-            "query": "transaction.duration:>5s",
-            "start": "2022-02-01",
-            "end": "2022-02-02",
-        }
-
-        expected_snuba_io = {
-            "query": "transaction.duration:>5s event.type:transaction",
-            "data": self.snuba_raw_data,
-            "granularity": 600,
-            "start": "2022-02-01 00:00:00",
-            "end": "2022-02-02 00:00:00",
-        }
-        self.do_request(data=request)
-
-        mock_get_anomalies.assert_called_once_with(expected_snuba_io)
-
-    @freeze_time("2022-02-11 03:21:34")
-    @mock.patch("sentry.api.endpoints.organization_transaction_anomaly_detection.timeseries_query")
-    @mock.patch("sentry.api.endpoints.organization_transaction_anomaly_detection.get_anomalies")
-    def test_get_stats_period(self, mock_get_anomalies, mock_timeseries_query):
-        SnubaTSResult = namedtuple("SnubaTSResult", "data")
-        mock_timeseries_query.return_value = SnubaTSResult(
-            data={"data": self.snuba_raw_data},
-        )
-        mock_get_anomalies.return_value = HttpResponse({"key": "value"})
-        request = {
-            "project": self.project.id,
-            "query": "transaction.duration:>5s",
-            "statsPeriod": "13h",
-        }
-
-        expected_snuba_io = {
-            "query": "transaction.duration:>5s event.type:transaction",
-            "data": self.snuba_raw_data,
-            "granularity": 600,
-            "start": "2022-02-10 14:21:34",
-            "end": "2022-02-11 03:21:34",
-        }
-        self.do_request(data=request)
-
-        mock_get_anomalies.assert_called_once_with(expected_snuba_io)
-
-    @mock.patch("sentry.api.endpoints.organization_transaction_anomaly_detection.timeseries_query")
-    @mock.patch("sentry.api.endpoints.organization_transaction_anomaly_detection.get_anomalies")
-    def test_get_no_query(self, mock_get_anomalies, mock_timeseries_query):
-        SnubaTSResult = namedtuple("SnubaTSResult", "data")
-        mock_timeseries_query.return_value = SnubaTSResult(
-            data={"data": self.snuba_raw_data},
-        )
-        mock_get_anomalies.return_value = HttpResponse({"key": "value"})
-        request = {
-            "project": self.project.id,
-            "start": "2022-01-01",
-            "end": "2022-01-05",
-        }
-
-        expected_snuba_io = {
-            "query": "event.type:transaction",
-            "data": self.snuba_raw_data,
-            "granularity": 1200,
-            "start": "2022-01-01 00:00:00",
-            "end": "2022-01-05 00:00:00",
-        }
-        self.do_request(data=request)
-
-        mock_get_anomalies.assert_called_once_with(expected_snuba_io)
-
-    @staticmethod
-    def test_get_time_params_600_granularity():
-        expected_tuple = (
-            datetime(2021, 12, 25, 12, 0, tzinfo=timezone.utc),
-            datetime(2022, 1, 8, 12, 0, tzinfo=timezone.utc),
-            600,
-        )
-        returned_tuple = get_time_params(
-            datetime(2022, 1, 1, 0, 0, tzinfo=timezone.utc),
-            datetime(2022, 1, 2, 0, 0, tzinfo=timezone.utc),
-        )
-        assert returned_tuple == expected_tuple
-
-    @staticmethod
-    def test_get_time_params_1200_granularity():
-        expected_tuple = (
-            datetime(2021, 12, 20, 0, 0, tzinfo=timezone.utc),
-            datetime(2022, 1, 17, 0, 0, tzinfo=timezone.utc),
-            1200,
-        )
-        returned_tuple = get_time_params(
-            datetime(2022, 1, 1, 0, 0, tzinfo=timezone.utc),
-            datetime(2022, 1, 5, 0, 0, tzinfo=timezone.utc),
-        )
-        assert returned_tuple == expected_tuple
-
-    @staticmethod
-    @freeze_time("2022-02-11 00:00:00")
-    def test_get_time_params_3600_granularity():
-        expected_tuple = (
-            datetime(2021, 11, 13, 0, 0, tzinfo=timezone.utc),
-            datetime(2022, 2, 11, 0, 0, tzinfo=timezone.utc),
-            3600,
-        )
-        returned_tuple = get_time_params(
-            datetime(2022, 1, 1, 0, 0, tzinfo=timezone.utc),
-            datetime(2022, 2, 5, 0, 0, tzinfo=timezone.utc),
-        )
-        assert returned_tuple == expected_tuple