Skip to content

Commit dbe033c

Browse files
authored
feat(dynamic-sampling): Improve check am2 compatibility (#53414)
1 parent ecbb360 commit dbe033c

File tree

2 files changed

+94
-6
lines changed

2 files changed

+94
-6
lines changed

src/sentry/tasks/check_am2_compatibility.py

+67-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from collections import defaultdict
22
from datetime import datetime, timedelta
33
from enum import Enum
4-
from typing import Any, Dict, Mapping, Optional, Set, Tuple
4+
from typing import Any, Dict, List, Mapping, Optional, Set, Tuple
55

66
import pytz
77
import sentry_sdk
@@ -10,11 +10,15 @@
1010
from sentry.dynamic_sampling import get_redis_client_for_ds
1111
from sentry.incidents.models import AlertRule
1212
from sentry.models import DashboardWidgetQuery, Organization, Project
13+
from sentry.snuba import metrics_performance
1314
from sentry.snuba.discover import query as discover_query
1415
from sentry.snuba.metrics_enhanced_performance import query as performance_query
1516
from sentry.tasks.base import instrumented_task
1617
from sentry.utils import json
1718

19+
# The time range over which the check script queries the data for determining the compatibility state.
20+
QUERY_TIME_RANGE_IN_DAYS = 1
21+
1822
# List of minimum SDK versions that support Performance at Scale.
1923
# The list is defined here:
2024
# https://docs.sentry.io/product/performance/performance-at-scale/getting-started
@@ -155,6 +159,9 @@
155159
# Match multiple tags that contain this.
156160
"stack.",
157161
"error.",
162+
# Match generic values.
163+
"issue",
164+
"exception",
158165
]
159166

160167

@@ -204,9 +211,14 @@ def compare_versions(cls, version1, version2):
204211

205212
@classmethod
206213
def format_results(
207-
cls, organization, unsupported_widgets, unsupported_alerts, outdated_sdks_per_project
214+
cls,
215+
organization,
216+
projects_compatibility,
217+
unsupported_widgets,
218+
unsupported_alerts,
219+
outdated_sdks_per_project,
208220
):
209-
results: Dict[str, Any] = {}
221+
results: Dict[str, Any] = {"projects_compatibility": projects_compatibility}
210222

211223
widgets = []
212224
for dashboard_id, unsupported_widgets in unsupported_widgets.items():
@@ -318,7 +330,7 @@ def get_sdks_version_used(cls, organization_id, project_objects):
318330
params = {
319331
"organization_id": organization_id,
320332
"project_objects": project_objects,
321-
"start": datetime.now(tz=pytz.UTC) - timedelta(days=1),
333+
"start": datetime.now(tz=pytz.UTC) - timedelta(days=QUERY_TIME_RANGE_IN_DAYS),
322334
"end": datetime.now(tz=pytz.UTC),
323335
}
324336

@@ -343,7 +355,7 @@ def is_metrics_data(cls, organization_id, project_objects, query):
343355
params = {
344356
"organization_id": organization_id,
345357
"project_objects": project_objects,
346-
"start": datetime.now(tz=pytz.UTC) - timedelta(days=1),
358+
"start": datetime.now(tz=pytz.UTC) - timedelta(days=QUERY_TIME_RANGE_IN_DAYS),
347359
"end": datetime.now(tz=pytz.UTC),
348360
}
349361

@@ -367,6 +379,7 @@ def get_excluded_conditions(cls):
367379
for condition in EXCLUDED_CONDITIONS:
368380
# We want to build an AND condition with multiple negated elements.
369381
qs &= ~Q(conditions__icontains=condition)
382+
qs &= ~Q(fields__icontains=condition)
370383

371384
return qs
372385

@@ -395,12 +408,56 @@ def get_all_alerts_of_organization(cls, organization_id):
395408
.values_list("id", "snuba_query__aggregate", "snuba_query__query")
396409
)
397410

411+
@classmethod
412+
def get_organization_metrics_compatibility(cls, organization, project_objects):
413+
data: Dict[str, List[Any]] = {
414+
"incompatible_projects": [],
415+
"compatible_projects": [],
416+
}
417+
418+
params = {
419+
"organization_id": organization.id,
420+
"project_objects": project_objects,
421+
"start": datetime.now(tz=pytz.UTC) - timedelta(days=QUERY_TIME_RANGE_IN_DAYS),
422+
"end": datetime.now(tz=pytz.UTC),
423+
}
424+
425+
project_ids = [project.id for project in project_objects]
426+
427+
count_has_txn = "count_has_transaction_name()"
428+
count_null = "count_null_transactions()"
429+
compatible_results = metrics_performance.query(
430+
selected_columns=[
431+
"project.id",
432+
count_null,
433+
count_has_txn,
434+
],
435+
params=params,
436+
query=f"{count_null}:0 AND {count_has_txn}:>0",
437+
referrer="api.organization-events",
438+
functions_acl=["count_null_transactions", "count_has_transaction_name"],
439+
use_aggregate_conditions=True,
440+
)
441+
442+
data["compatible_projects"] = sorted(
443+
row["project.id"] for row in compatible_results["data"]
444+
)
445+
data["incompatible_projects"] = sorted(
446+
list(set(project_ids) - set(data["compatible_projects"]))
447+
)
448+
449+
return data
450+
398451
@classmethod
399452
def run_compatibility_check(cls, org_id):
400453
organization = Organization.objects.get(id=org_id)
401454

402455
all_projects = list(Project.objects.using_replica().filter(organization=organization))
403456

457+
projects_compatibility = cls.get_organization_metrics_compatibility(
458+
organization, all_projects
459+
)
460+
404461
unsupported_widgets = defaultdict(list)
405462
for (
406463
widget_id,
@@ -455,7 +512,11 @@ def run_compatibility_check(cls, org_id):
455512
outdated_sdks_per_project = {}
456513

457514
return cls.format_results(
458-
organization, unsupported_widgets, unsupported_alerts, outdated_sdks_per_project
515+
organization,
516+
projects_compatibility,
517+
unsupported_widgets,
518+
unsupported_alerts,
519+
outdated_sdks_per_project,
459520
)
460521

461522

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from unittest.mock import patch
2+
3+
import pytest
4+
5+
from sentry.tasks.check_am2_compatibility import (
6+
CheckStatus,
7+
get_check_status,
8+
run_compatibility_check_async,
9+
)
10+
from sentry.testutils import TestCase
11+
12+
pytestmark = pytest.mark.sentry_metrics
13+
14+
15+
class CheckAM2CompatibilityTest(TestCase):
16+
def test_check_with_success(self):
17+
with self.tasks():
18+
run_compatibility_check_async(org_id=self.organization.id)
19+
assert get_check_status(self.organization.id) == CheckStatus.DONE
20+
21+
@patch("sentry.tasks.check_am2_compatibility.CheckAM2Compatibility.run_compatibility_check")
22+
def test_check_with_error(self, run_compatibility_check):
23+
run_compatibility_check.side_effect = Exception
24+
25+
with self.tasks():
26+
run_compatibility_check_async(org_id=self.organization.id)
27+
assert get_check_status(self.organization.id) == CheckStatus.ERROR

0 commit comments

Comments
 (0)