Browse Source

feat(metrics): add org option to activate percentiles for distributions in metrics (#71554)

Simon Hellmayr 9 months ago
parent
commit
71ef3edd60

+ 16 - 0
src/sentry/api/endpoints/organization_details.py

@@ -41,6 +41,8 @@ from sentry.constants import (
     JOIN_REQUESTS_DEFAULT,
     LEGACY_RATE_LIMIT_OPTIONS,
     METRIC_ALERTS_THREAD_DEFAULT,
+    METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT,
+    METRICS_ACTIVATE_PERCENTILES_DEFAULT,
     PROJECT_RATE_LIMIT_DEFAULT,
     REQUIRE_SCRUB_DATA_DEFAULT,
     REQUIRE_SCRUB_DEFAULTS_DEFAULT,
@@ -190,6 +192,18 @@ ORG_OPTIONS = (
         bool,
         METRIC_ALERTS_THREAD_DEFAULT,
     ),
+    (
+        "metricsActivatePercentiles",
+        "sentry:metrics_activate_percentiles",
+        bool,
+        METRICS_ACTIVATE_PERCENTILES_DEFAULT,
+    ),
+    (
+        "metricsActivateLastForGauges",
+        "sentry:metrics_activate_last_for_gauges",
+        bool,
+        METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT,
+    ),
 )
 
 DELETION_STATUSES = frozenset(
@@ -238,6 +252,8 @@ class OrganizationSerializer(BaseOrganizationSerializer):
     githubPRBot = serializers.BooleanField(required=False)
     issueAlertsThreadFlag = serializers.BooleanField(required=False)
     metricAlertsThreadFlag = serializers.BooleanField(required=False)
+    metricsActivatePercentiles = serializers.BooleanField(required=False)
+    metricsActivateLastForGauges = serializers.BooleanField(required=False)
     aggregatedDataConsent = serializers.BooleanField(required=False)
     genAIConsent = serializers.BooleanField(required=False)
     require2FA = serializers.BooleanField(required=False)

+ 15 - 0
src/sentry/api/serializers/models/organization.py

@@ -36,6 +36,8 @@ from sentry.constants import (
     ISSUE_ALERTS_THREAD_DEFAULT,
     JOIN_REQUESTS_DEFAULT,
     METRIC_ALERTS_THREAD_DEFAULT,
+    METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT,
+    METRICS_ACTIVATE_PERCENTILES_DEFAULT,
     PROJECT_RATE_LIMIT_DEFAULT,
     REQUIRE_SCRUB_DATA_DEFAULT,
     REQUIRE_SCRUB_DEFAULTS_DEFAULT,
@@ -433,6 +435,8 @@ class DetailedOrganizationSerializerResponse(_DetailedOrganizationSerializerResp
     isDynamicallySampled: bool
     issueAlertsThreadFlag: bool
     metricAlertsThreadFlag: bool
+    metricsActivatePercentiles: bool
+    metricsActivateLastForGauges: bool
 
 
 class DetailedOrganizationSerializer(OrganizationSerializer):
@@ -557,6 +561,17 @@ class DetailedOrganizationSerializer(OrganizationSerializer):
                 "metricAlertsThreadFlag": bool(
                     obj.get_option("sentry:metric_alerts_thread_flag", METRIC_ALERTS_THREAD_DEFAULT)
                 ),
+                "metricsActivatePercentiles": bool(
+                    obj.get_option(
+                        "sentry:metrics_activate_percentiles", METRICS_ACTIVATE_PERCENTILES_DEFAULT
+                    )
+                ),
+                "metricsActivateLastForGauges": bool(
+                    obj.get_option(
+                        "sentry:metrics_activate_last_for_gauges",
+                        METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT,
+                    )
+                ),
             }
         )
 

+ 2 - 0
src/sentry/constants.py

@@ -645,6 +645,8 @@ AI_SUGGESTED_SOLUTION = True
 GITHUB_COMMENT_BOT_DEFAULT = True
 ISSUE_ALERTS_THREAD_DEFAULT = True
 METRIC_ALERTS_THREAD_DEFAULT = True
+METRICS_ACTIVATE_PERCENTILES_DEFAULT = False
+METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT = False
 DATA_CONSENT_DEFAULT = False
 
 # `sentry:events_member_admin` - controls whether the 'member' role gets the event:admin scope

+ 15 - 9
src/sentry/sentry_metrics/querying/metadata/metrics.py

@@ -2,7 +2,11 @@ from collections import defaultdict
 from collections.abc import Sequence
 from typing import cast
 
-from sentry import options
+from sentry.constants import (
+    METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT,
+    METRICS_ACTIVATE_PERCENTILES_DEFAULT,
+)
+from sentry.models.options import OrganizationOption
 from sentry.models.organization import Organization
 from sentry.models.project import Project
 from sentry.sentry_metrics.querying.metadata.utils import (
@@ -88,14 +92,16 @@ def get_metrics_meta(
 
 def generate_operations_config(organization: Organization) -> OperationsConfiguration:
     operations_config = OperationsConfiguration()
-    configuration_options = [
-        "sentry-metrics.metrics-api.enable-percentile-operations-for-orgs",
-        "sentry-metrics.metrics-api.enable-gauge-last-for-orgs",
-    ]
-
-    for option in configuration_options:
-        if organization.id not in options.get(option):
-            operations_config.hide_operations(METRICS_API_HIDDEN_OPERATIONS[option])
+    configuration_options: dict[str, bool] = {
+        "sentry:metrics_activate_percentiles": METRICS_ACTIVATE_PERCENTILES_DEFAULT,
+        "sentry:metrics_activate_last_for_gauges": METRICS_ACTIVATE_LAST_FOR_GAUGES_DEFAULT,
+    }
+
+    for option_key, default_value in configuration_options.items():
+        if not OrganizationOption.objects.get_value(
+            organization=organization, key=option_key, default=default_value
+        ):
+            operations_config.hide_operations(METRICS_API_HIDDEN_OPERATIONS[option_key])
 
     return operations_config
 

+ 2 - 2
src/sentry/sentry_metrics/querying/metadata/utils.py

@@ -2,14 +2,14 @@ from sentry.snuba.metrics import get_mri
 from sentry.snuba.metrics.naming_layer.mri import is_mri
 
 METRICS_API_HIDDEN_OPERATIONS = {
-    "sentry-metrics.metrics-api.enable-percentile-operations-for-orgs": [
+    "sentry:metrics_activate_percentiles": [
         "p50",
         "p75",
         "p90",
         "p95",
         "p99",
     ],
-    "sentry-metrics.metrics-api.enable-gauge-last-for-orgs": ["last"],
+    "sentry:metrics_activate_last_for_gauges": ["last"],
 }
 
 

+ 12 - 0
tests/sentry/api/endpoints/test_organization_details.py

@@ -428,6 +428,8 @@ class OrganizationUpdateTest(OrganizationDetailsTestBase):
             "genAIConsent": True,
             "issueAlertsThreadFlag": False,
             "metricAlertsThreadFlag": False,
+            "metricsActivatePercentiles": True,
+            "metricsActivateLastForGauges": True,
         }
 
         # needed to set require2FA
@@ -461,6 +463,8 @@ class OrganizationUpdateTest(OrganizationDetailsTestBase):
         assert options.get("sentry:scrape_javascript") is False
         assert options.get("sentry:join_requests") is False
         assert options.get("sentry:events_member_admin") is False
+        assert options.get("sentry:metrics_activate_percentiles") is True
+        assert options.get("sentry:metrics_activate_last_for_gauges") is True
 
         # log created
         with assume_test_silo_mode_of(AuditLogEntry):
@@ -493,6 +497,14 @@ class OrganizationUpdateTest(OrganizationDetailsTestBase):
         assert "to {}".format(data["genAIConsent"]) in log.data["genAIConsent"]
         assert "to {}".format(data["issueAlertsThreadFlag"]) in log.data["issueAlertsThreadFlag"]
         assert "to {}".format(data["metricAlertsThreadFlag"]) in log.data["metricAlertsThreadFlag"]
+        assert (
+            "to {}".format(data["metricsActivatePercentiles"])
+            in log.data["metricsActivatePercentiles"]
+        )
+        assert (
+            "to {}".format(data["metricsActivateLastForGauges"])
+            in log.data["metricsActivateLastForGauges"]
+        )
 
     @responses.activate
     @patch(

+ 66 - 44
tests/sentry/api/endpoints/test_organization_metrics_details.py

@@ -9,7 +9,6 @@ from sentry.sentry_metrics.use_case_id_registry import (
 )
 from sentry.sentry_metrics.visibility import block_metric, block_tags_of_metric
 from sentry.testutils.cases import MetricsAPIBaseTestCase, OrganizationMetricsIntegrationTestCase
-from sentry.testutils.helpers import override_options
 from sentry.testutils.skips import requires_snuba
 
 pytestmark = [pytest.mark.sentry_metrics, requires_snuba]
@@ -241,46 +240,69 @@ class OrganizationMetricsDetailsTest(OrganizationMetricsIntegrationTestCase):
             "sum",
         ]
 
-        with override_options(
-            {
-                "sentry-metrics.metrics-api.enable-percentile-operations-for-orgs": [
-                    self.organization.id
-                ]
-            },
-        ):
-            response = self.get_success_response(
-                self.organization.slug, project=[project_1.id, project_2.id], useCase="custom"
-            )
-            data = sorted(response.data, key=lambda d: d["mri"])
-            assert sorted(data[1]["operations"]) == [
-                "avg",
-                "count",
-                "histogram",
-                "max",
-                "max_timestamp",
-                "min",
-                "min_timestamp",
-                "p50",
-                "p75",
-                "p90",
-                "p95",
-                "p99",
-                "sum",
-            ]
-
-            with override_options(
-                {"sentry-metrics.metrics-api.enable-gauge-last-for-orgs": [self.organization.id]},
-            ):
-                response = self.get_success_response(
-                    self.organization.slug, project=[project_1.id, project_2.id], useCase="custom"
-                )
-                data = sorted(response.data, key=lambda d: d["mri"])
-
-                assert sorted(data[2]["operations"]) == [
-                    "avg",
-                    "count",
-                    "last",
-                    "max",
-                    "min",
-                    "sum",
-                ]
+        # test default deactivated percentiles
+        response = self.get_success_response(
+            self.organization.slug, project=[project_1.id, project_2.id], useCase="custom"
+        )
+        data = sorted(response.data, key=lambda d: d["mri"])
+        assert sorted(data[1]["operations"]) == [
+            "avg",
+            "count",
+            "histogram",
+            "max",
+            "max_timestamp",
+            "min",
+            "min_timestamp",
+            "sum",
+        ]
+
+        # test activated percentiles
+        self.organization.update_option("sentry:metrics_activate_percentiles", True)
+        response = self.get_success_response(
+            self.organization.slug, project=[project_1.id, project_2.id], useCase="custom"
+        )
+        data = sorted(response.data, key=lambda d: d["mri"])
+        assert sorted(data[1]["operations"]) == [
+            "avg",
+            "count",
+            "histogram",
+            "max",
+            "max_timestamp",
+            "min",
+            "min_timestamp",
+            "p50",
+            "p75",
+            "p90",
+            "p95",
+            "p99",
+            "sum",
+        ]
+
+        # test default deactivated gauges
+        response = self.get_success_response(
+            self.organization.slug, project=[project_1.id, project_2.id], useCase="custom"
+        )
+        data = sorted(response.data, key=lambda d: d["mri"])
+        assert sorted(data[2]["operations"]) == [
+            "avg",
+            "count",
+            "max",
+            "min",
+            "sum",
+        ]
+
+        # test activated gauges
+        self.organization.update_option("sentry:metrics_activate_last_for_gauges", True)
+        response = self.get_success_response(
+            self.organization.slug, project=[project_1.id, project_2.id], useCase="custom"
+        )
+        data = sorted(response.data, key=lambda d: d["mri"])
+
+        assert sorted(data[2]["operations"]) == [
+            "avg",
+            "count",
+            "last",
+            "max",
+            "min",
+            "sum",
+        ]