|
@@ -2,6 +2,7 @@ import os
|
|
|
import unittest
|
|
|
from unittest.mock import Mock, call, patch
|
|
|
|
|
|
+from sentry import projectoptions
|
|
|
from sentry.eventstore.models import Event
|
|
|
from sentry.testutils import TestCase
|
|
|
from sentry.testutils.helpers import override_options
|
|
@@ -63,6 +64,31 @@ def create_event(spans, event_id="a" * 16):
|
|
|
return {"event_id": event_id, "project": PROJECT_ID, "spans": spans}
|
|
|
|
|
|
|
|
|
+def assert_n_plus_one_db_problem(perf_problems):
|
|
|
+ assert perf_problems == [
|
|
|
+ PerformanceProblem(
|
|
|
+ fingerprint="1-GroupType.PERFORMANCE_N_PLUS_ONE_DB_QUERIES-8d86357da4d8a866b19c97670edee38d037a7bc8",
|
|
|
+ op="db",
|
|
|
+ desc="SELECT `books_author`.`id`, `books_author`.`name` FROM `books_author` WHERE `books_author`.`id` = %s LIMIT 21",
|
|
|
+ type=GroupType.PERFORMANCE_N_PLUS_ONE_DB_QUERIES,
|
|
|
+ parent_span_ids=["8dd7a5869a4f4583"],
|
|
|
+ cause_span_ids=["9179e43ae844b174"],
|
|
|
+ offender_span_ids=[
|
|
|
+ "b8be6138369491dd",
|
|
|
+ "b2d4826e7b618f1b",
|
|
|
+ "b3fdeea42536dbf1",
|
|
|
+ "b409e78a092e642f",
|
|
|
+ "86d2ede57bbf48d4",
|
|
|
+ "8e554c84cdc9731e",
|
|
|
+ "94d6230f3f910e12",
|
|
|
+ "a210b87a2191ceb6",
|
|
|
+ "88a5ccaf25b9bd8f",
|
|
|
+ "bb32cf50fc56b296",
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
class PerformanceDetectionTest(unittest.TestCase):
|
|
|
def setUp(self):
|
|
|
super().setUp()
|
|
@@ -70,6 +96,24 @@ class PerformanceDetectionTest(unittest.TestCase):
|
|
|
self.project_option_mock = patch_project_option_get.start()
|
|
|
self.addCleanup(patch_project_option_get.stop)
|
|
|
|
|
|
+ patch_project = patch("sentry.models.Project.objects.get_from_cache")
|
|
|
+ self.project_mock = patch_project.start()
|
|
|
+ self.addCleanup(patch_project.stop)
|
|
|
+
|
|
|
+ patch_organization = patch("sentry.models.Organization.objects.get_from_cache")
|
|
|
+ self.organization_mock = patch_organization.start()
|
|
|
+ self.addCleanup(patch_organization.stop)
|
|
|
+
|
|
|
+ self.features = ["organizations:performance-issues-ingest"]
|
|
|
+
|
|
|
+ def has_feature(feature, org):
|
|
|
+ return feature in self.features
|
|
|
+
|
|
|
+ patch_features = patch("sentry.features.has")
|
|
|
+ self.features_mock = patch_features.start()
|
|
|
+ self.features_mock.side_effect = has_feature
|
|
|
+ self.addCleanup(patch_features.stop)
|
|
|
+
|
|
|
@patch("sentry.utils.performance_issues.performance_detection._detect_performance_problems")
|
|
|
def test_options_disabled(self, mock):
|
|
|
event = {}
|
|
@@ -84,33 +128,13 @@ class PerformanceDetectionTest(unittest.TestCase):
|
|
|
detect_performance_problems(event)
|
|
|
assert mock.call_count == 1
|
|
|
|
|
|
+ @override_options({"performance.issues.n_plus_one_db.problem-creation": 1.0})
|
|
|
def test_project_option_overrides_default(self):
|
|
|
n_plus_one_event = EVENTS["n-plus-one-in-django-index-view"]
|
|
|
sdk_span_mock = Mock()
|
|
|
|
|
|
perf_problems = _detect_performance_problems(n_plus_one_event, sdk_span_mock)
|
|
|
- assert perf_problems == [
|
|
|
- PerformanceProblem(
|
|
|
- fingerprint="1-GroupType.PERFORMANCE_N_PLUS_ONE_DB_QUERIES-8d86357da4d8a866b19c97670edee38d037a7bc8",
|
|
|
- op="db",
|
|
|
- desc="SELECT `books_author`.`id`, `books_author`.`name` FROM `books_author` WHERE `books_author`.`id` = %s LIMIT 21",
|
|
|
- type=GroupType.PERFORMANCE_N_PLUS_ONE_DB_QUERIES,
|
|
|
- parent_span_ids=["8dd7a5869a4f4583"],
|
|
|
- cause_span_ids=["9179e43ae844b174"],
|
|
|
- offender_span_ids=[
|
|
|
- "b8be6138369491dd",
|
|
|
- "b2d4826e7b618f1b",
|
|
|
- "b3fdeea42536dbf1",
|
|
|
- "b409e78a092e642f",
|
|
|
- "86d2ede57bbf48d4",
|
|
|
- "8e554c84cdc9731e",
|
|
|
- "94d6230f3f910e12",
|
|
|
- "a210b87a2191ceb6",
|
|
|
- "88a5ccaf25b9bd8f",
|
|
|
- "bb32cf50fc56b296",
|
|
|
- ],
|
|
|
- )
|
|
|
- ]
|
|
|
+ assert_n_plus_one_db_problem(perf_problems)
|
|
|
|
|
|
self.project_option_mock.return_value = {
|
|
|
"n_plus_one_db_duration_threshold": 100000,
|
|
@@ -119,6 +143,49 @@ class PerformanceDetectionTest(unittest.TestCase):
|
|
|
perf_problems = _detect_performance_problems(n_plus_one_event, sdk_span_mock)
|
|
|
assert perf_problems == []
|
|
|
|
|
|
+ @override_options({"performance.issues.n_plus_one_db.problem-creation": 1.0})
|
|
|
+ def test_no_feature_flag_disables_creation(self):
|
|
|
+ self.features = []
|
|
|
+ n_plus_one_event = EVENTS["n-plus-one-in-django-index-view"]
|
|
|
+ sdk_span_mock = Mock()
|
|
|
+
|
|
|
+ perf_problems = _detect_performance_problems(n_plus_one_event, sdk_span_mock)
|
|
|
+ assert perf_problems == []
|
|
|
+
|
|
|
+ @override_options({"performance.issues.n_plus_one_db.problem-creation": 0.0})
|
|
|
+ def test_system_option_disables_detector_issue_creation(self):
|
|
|
+ n_plus_one_event = EVENTS["n-plus-one-in-django-index-view"]
|
|
|
+ sdk_span_mock = Mock()
|
|
|
+
|
|
|
+ perf_problems = _detect_performance_problems(n_plus_one_event, sdk_span_mock)
|
|
|
+ assert perf_problems == []
|
|
|
+
|
|
|
+ @override_options({"performance.issues.n_plus_one_db.problem-creation": 1.0})
|
|
|
+ def test_system_option_used_when_project_option_is_default(self):
|
|
|
+ n_plus_one_event = EVENTS["n-plus-one-in-django-index-view"]
|
|
|
+ sdk_span_mock = Mock()
|
|
|
+
|
|
|
+ self.project_option_mock.return_value = projectoptions.get_well_known_default(
|
|
|
+ "sentry:performance_issue_settings", project=1
|
|
|
+ )
|
|
|
+ with override_options(
|
|
|
+ {
|
|
|
+ "performance.issues.n_plus_one_db.count_threshold": 20,
|
|
|
+ "performance.issues.n_plus_one_db.duration_threshold": 100,
|
|
|
+ }
|
|
|
+ ):
|
|
|
+ perf_problems = _detect_performance_problems(n_plus_one_event, sdk_span_mock)
|
|
|
+ assert perf_problems == []
|
|
|
+
|
|
|
+ with override_options(
|
|
|
+ {
|
|
|
+ "performance.issues.n_plus_one_db.count_threshold": 5,
|
|
|
+ "performance.issues.n_plus_one_db.duration_threshold": 100,
|
|
|
+ }
|
|
|
+ ):
|
|
|
+ perf_problems = _detect_performance_problems(n_plus_one_event, sdk_span_mock)
|
|
|
+ assert_n_plus_one_db_problem(perf_problems)
|
|
|
+
|
|
|
def test_calls_detect_duplicate(self):
|
|
|
no_duplicate_event = create_event([create_span("db")] * 4)
|
|
|
duplicate_not_allowed_op_event = create_event([create_span("random", 100.0)] * 5)
|
|
@@ -492,6 +559,7 @@ class PerformanceDetectionTest(unittest.TestCase):
|
|
|
|
|
|
assert sdk_span_mock.containing_transaction.set_tag.call_count == 0
|
|
|
|
|
|
+ @override_options({"performance.issues.n_plus_one_db.problem-creation": 1.0})
|
|
|
def test_detects_multiple_performance_issues_in_n_plus_one_query(self):
|
|
|
n_plus_one_event = EVENTS["n-plus-one-in-django-index-view"]
|
|
|
sdk_span_mock = Mock()
|
|
@@ -525,28 +593,7 @@ class PerformanceDetectionTest(unittest.TestCase):
|
|
|
call("_pi_n_plus_one_db", "b8be6138369491dd"),
|
|
|
]
|
|
|
)
|
|
|
- assert perf_problems == [
|
|
|
- PerformanceProblem(
|
|
|
- fingerprint="1-GroupType.PERFORMANCE_N_PLUS_ONE_DB_QUERIES-8d86357da4d8a866b19c97670edee38d037a7bc8",
|
|
|
- op="db",
|
|
|
- desc="SELECT `books_author`.`id`, `books_author`.`name` FROM `books_author` WHERE `books_author`.`id` = %s LIMIT 21",
|
|
|
- type=GroupType.PERFORMANCE_N_PLUS_ONE_DB_QUERIES,
|
|
|
- parent_span_ids=["8dd7a5869a4f4583"],
|
|
|
- cause_span_ids=["9179e43ae844b174"],
|
|
|
- offender_span_ids=[
|
|
|
- "b8be6138369491dd",
|
|
|
- "b2d4826e7b618f1b",
|
|
|
- "b3fdeea42536dbf1",
|
|
|
- "b409e78a092e642f",
|
|
|
- "86d2ede57bbf48d4",
|
|
|
- "8e554c84cdc9731e",
|
|
|
- "94d6230f3f910e12",
|
|
|
- "a210b87a2191ceb6",
|
|
|
- "88a5ccaf25b9bd8f",
|
|
|
- "bb32cf50fc56b296",
|
|
|
- ],
|
|
|
- )
|
|
|
- ]
|
|
|
+ assert_n_plus_one_db_problem(perf_problems)
|
|
|
|
|
|
@patch("sentry.utils.metrics.incr")
|
|
|
def test_does_not_report_metric_on_non_truncated_n_plus_one_query(self, incr_mock):
|