From c643013ddf4193cd6f5de0f363ffa46571e292e9 Mon Sep 17 00:00:00 2001 From: Cathy Teng Date: Mon, 24 Nov 2025 11:30:55 -0800 Subject: [PATCH 1/2] filter out orphaned metric alerts when checking limit --- .../endpoints/organization_alert_rule_index.py | 6 +++--- .../test_organization_alert_rule_index.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/sentry/incidents/endpoints/organization_alert_rule_index.py b/src/sentry/incidents/endpoints/organization_alert_rule_index.py index 78dd443747cb6b..a06f203a785566 100644 --- a/src/sentry/incidents/endpoints/organization_alert_rule_index.py +++ b/src/sentry/incidents/endpoints/organization_alert_rule_index.py @@ -605,9 +605,9 @@ def check_can_create_alert(self, request: Request, organization: Organization) - "organizations:workflow-engine-metric-detector-limit", organization, actor=request.user ): alert_limit = quotas.backend.get_metric_detector_limit(organization.id) - alert_count = AlertRule.objects.fetch_for_organization( - organization=organization - ).count() + alert_count = AlertRule.objects.fetch_for_organization(organization=organization) + # filter out alert rules without any projects + alert_count = alert_count.filter(projects__isnull=False).count() if alert_limit >= 0 and alert_count >= alert_limit: raise ValidationError( diff --git a/tests/sentry/incidents/endpoints/test_organization_alert_rule_index.py b/tests/sentry/incidents/endpoints/test_organization_alert_rule_index.py index 97255185232a34..24b40d04cd2b17 100644 --- a/tests/sentry/incidents/endpoints/test_organization_alert_rule_index.py +++ b/tests/sentry/incidents/endpoints/test_organization_alert_rule_index.py @@ -1612,6 +1612,13 @@ def test_performance_alert(self, record_analytics: MagicMock) -> None: @with_feature("organizations:workflow-engine-metric-detector-limit") @patch("sentry.quotas.backend.get_metric_detector_limit") def test_metric_alert_limit(self, mock_get_limit: MagicMock) -> None: + # create orphaned metric alert + project_to_delete = self.create_project(organization=self.organization) + alert_rule = self.create_alert_rule( + organization=self.organization, projects=[project_to_delete] + ) + project_to_delete.delete() + # Set limit to 2 alert rules mock_get_limit.return_value = 2 @@ -1646,6 +1653,13 @@ def test_metric_alert_limit(self, mock_get_limit: MagicMock) -> None: @with_feature("organizations:incidents") @with_feature("organizations:workflow-engine-metric-detector-limit") def test_metric_alert_limit_unlimited_plan(self) -> None: + # create orphaned metric alert + project_to_delete = self.create_project(organization=self.organization) + alert_rule = self.create_alert_rule( + organization=self.organization, projects=[project_to_delete] + ) + project_to_delete.delete() + # Create many alert rules for _ in range(5): self.create_alert_rule(organization=self.organization) From 6f6f5bfdbc75f103bded20a3a068823e9b4ac194 Mon Sep 17 00:00:00 2001 From: Cathy Teng Date: Mon, 24 Nov 2025 11:35:14 -0800 Subject: [PATCH 2/2] distinct --- src/sentry/incidents/endpoints/organization_alert_rule_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/incidents/endpoints/organization_alert_rule_index.py b/src/sentry/incidents/endpoints/organization_alert_rule_index.py index a06f203a785566..431b0e48c1c353 100644 --- a/src/sentry/incidents/endpoints/organization_alert_rule_index.py +++ b/src/sentry/incidents/endpoints/organization_alert_rule_index.py @@ -607,7 +607,7 @@ def check_can_create_alert(self, request: Request, organization: Organization) - alert_limit = quotas.backend.get_metric_detector_limit(organization.id) alert_count = AlertRule.objects.fetch_for_organization(organization=organization) # filter out alert rules without any projects - alert_count = alert_count.filter(projects__isnull=False).count() + alert_count = alert_count.filter(projects__isnull=False).distinct().count() if alert_limit >= 0 and alert_count >= alert_limit: raise ValidationError(