Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(perf-issues): Add CLI script for testing detector output #39727

Merged
merged 11 commits into from
Oct 19, 2022
39 changes: 39 additions & 0 deletions bin/detect-performance
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python

import click

from sentry.runner import configure
from sentry.utils import json

configure()


@click.command()
@click.argument("filename", type=click.Path(exists=True))
def main(filename):
"""Runs performance detection on event data in the supplied filename using
default detector settings. Filename should be a JSON file."""
from sentry.utils.performance_issues.performance_detection import (
NPlusOneDBSpanDetectorExtended,
get_detection_settings,
run_detector_on_data,
)

settings = get_detection_settings()

with open(filename) as file:
data = json.loads(file.read())

detector = NPlusOneDBSpanDetectorExtended(settings, data)

run_detector_on_data(detector, data)

if len(detector.stored_problems) == 0:
click.echo("No problems detected")

for problem in detector.stored_problems.values():
click.echo(problem.to_dict())


if __name__ == "__main__":
main()
35 changes: 24 additions & 11 deletions src/sentry/utils/performance_issues/performance_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,22 @@ def detect_performance_problems(data: Event) -> List[PerformanceProblem]:
# Gets the thresholds to perform performance detection.
# Duration thresholds are in milliseconds.
# Allowed span ops are allowed span prefixes. (eg. 'http' would work for a span with 'http.client' as its op)
def get_detection_settings(project_id: str):
default_project_settings = projectoptions.get_well_known_default(
"sentry:performance_issue_settings",
project=project_id,
def get_detection_settings(project_id: Optional[str] = None):
default_project_settings = (
projectoptions.get_well_known_default(
"sentry:performance_issue_settings",
project=project_id,
)
if project_id
else {}
)
project_settings = ProjectOption.objects.get_value(
project_id, "sentry:performance_issue_settings", default_project_settings

project_settings = (
ProjectOption.objects.get_value(
project_id, "sentry:performance_issue_settings", default_project_settings
)
if project_id
else {}
)

use_project_option_settings = default_project_settings != project_settings
Expand Down Expand Up @@ -282,7 +291,6 @@ def get_detection_settings(project_id: str):

def _detect_performance_problems(data: Event, sdk_span: Any) -> List[PerformanceProblem]:
event_id = data.get("event_id", None)
spans = data.get("spans", [])
project_id = data.get("project")

detection_settings = get_detection_settings(project_id)
Expand All @@ -302,11 +310,8 @@ def _detect_performance_problems(data: Event, sdk_span: Any) -> List[Performance
),
}

for span in spans:
for _, detector in detectors.items():
detector.visit_span(span)
for _, detector in detectors.items():
detector.on_complete()
run_detector_on_data(detector, data)

# Metrics reporting only for detection, not created issues.
report_metrics_for_detectors(data, event_id, detectors, sdk_span)
Expand Down Expand Up @@ -341,6 +346,14 @@ def _detect_performance_problems(data: Event, sdk_span: Any) -> List[Performance
return list(unique_performance_problems)


def run_detector_on_data(detector, data):
spans = data.get("spans", [])
for span in spans:
detector.visit_span(span)

detector.on_complete()


# Uses options and flags to determine which orgs and which detectors automatically create performance issues.
def get_allowed_issue_creation_detectors(project_id: str):
project = Project.objects.get_from_cache(id=project_id)
Expand Down