Просмотр исходного кода

feat(grouping): Enable secondary grouping with upgrade (#27828)

When upgrading to a new grouping strategy, keep the old strategy alive
as secondary grouping for 90 days in order to reduce the number of newly
created groups.

Fix https://getsentry.atlassian.net/browse/INGEST-216
Joris Bayer 3 лет назад
Родитель
Сommit
52e132edc0

+ 6 - 1
src/sentry/api/endpoints/project_details.py

@@ -260,11 +260,16 @@ class ProjectAdminSerializer(ProjectMemberSerializer):
             raise serializers.ValidationError(
                 f"Grouping expiry must be a numerical value, a UNIX timestamp with second resolution, found {type(value)}"
             )
-        if not (0 < value - time.time() < (91 * 24 * 3600)):
+        now = time.time()
+        if value < now:
             raise serializers.ValidationError(
                 "Grouping expiry must be sometime within the next 90 days and not in the past. Perhaps you specified the timestamp not in seconds?"
             )
 
+        max_expiry_date = now + (91 * 24 * 3600)
+        if value > max_expiry_date:
+            value = max_expiry_date
+
         return value
 
     def validate_fingerprintingRules(self, value):

+ 3 - 0
src/sentry/api/serializers/models/project.py

@@ -650,6 +650,8 @@ class DetailedProjectSerializer(ProjectWithTeamSerializer):
             "sentry:grouping_config",
             "sentry:grouping_enhancements",
             "sentry:grouping_enhancements_base",
+            "sentry:secondary_grouping_config",
+            "sentry:secondary_grouping_expiry",
             "sentry:fingerprinting_rules",
             "sentry:relay_pii_config",
             "sentry:dynamic_sampling",
@@ -795,6 +797,7 @@ class DetailedProjectSerializer(ProjectWithTeamSerializer):
                 "dynamicSampling": get_value_with_default("sentry:dynamic_sampling"),
             }
         )
+
         return data
 
 

+ 3 - 1
src/sentry/event_manager.py

@@ -356,7 +356,9 @@ class EventManager:
         secondary_hashes = None
 
         try:
-            if (project.get_option("sentry:secondary_grouping_expiry") or 0) >= time.time():
+            secondary_grouping_config = project.get_option("sentry:secondary_grouping_config")
+            secondary_grouping_expiry = project.get_option("sentry:secondary_grouping_expiry")
+            if secondary_grouping_config and (secondary_grouping_expiry or 0) >= time.time():
                 with metrics.timer("event_manager.secondary_grouping"):
                     secondary_event = copy.deepcopy(job["event"])
                     loader = SecondaryGroupingConfigLoader()

+ 6 - 1
static/app/views/settings/projectIssueGrouping/upgradeGrouping.tsx

@@ -41,9 +41,14 @@ function UpgradeGrouping({
   const {riskNote, alertType} = getGroupingRisk(riskLevel);
   const noUpdates = !latestGroupingConfig;
 
-  const newData: Record<string, string> = {};
+  const newData: Record<string, string | number> = {};
   if (latestGroupingConfig) {
+    const now = Math.floor(new Date().getTime() / 1000);
+    const ninety_days = 3600 * 24 * 90;
+
     newData.groupingConfig = latestGroupingConfig.id;
+    newData.secondaryGroupingConfig = project.groupingConfig;
+    newData.secondaryGroupingExpiry = now + ninety_days;
   }
 
   const handleUpgrade = async () => {

+ 28 - 0
tests/sentry/api/endpoints/test_project_details.py

@@ -1,3 +1,5 @@
+from time import time
+
 import pytest
 
 from sentry.api.endpoints.project_details import (
@@ -722,6 +724,32 @@ class ProjectUpdateTest(APITestCase):
         new_next_id = saved_config["next_id"]
         assert new_next_id == 7
 
+    def test_cap_secondary_grouping_expiry(self):
+        now = time()
+
+        response = self.get_response(self.org_slug, self.proj_slug, secondaryGroupingExpiry=0)
+        assert response.status_code == 400
+
+        expiry = int(now + 3600 * 24 * 1)
+        response = self.get_valid_response(
+            self.org_slug, self.proj_slug, secondaryGroupingExpiry=expiry
+        )
+        assert response.data["secondaryGroupingExpiry"] == expiry
+
+        expiry = int(now + 3600 * 24 * 89)
+        response = self.get_valid_response(
+            self.org_slug, self.proj_slug, secondaryGroupingExpiry=expiry
+        )
+        assert response.data["secondaryGroupingExpiry"] == expiry
+
+        # Larger timestamps are capped to 91 days:
+        expiry = int(now + 3600 * 24 * 365)
+        response = self.get_valid_response(
+            self.org_slug, self.proj_slug, secondaryGroupingExpiry=expiry
+        )
+        expiry = response.data["secondaryGroupingExpiry"]
+        assert (now + 3600 * 24 * 90) < expiry < (now + 3600 * 24 * 92)
+
 
 class CopyProjectSettingsTest(APITestCase):
     endpoint = "sentry-api-0-project-details"