diff --git a/src/sentry/api/endpoints/organization_profiling_profiles.py b/src/sentry/api/endpoints/organization_profiling_profiles.py index 46fb9e0217b46f..4e9e20a41f489e 100644 --- a/src/sentry/api/endpoints/organization_profiling_profiles.py +++ b/src/sentry/api/endpoints/organization_profiling_profiles.py @@ -1,4 +1,3 @@ -import sentry_sdk from django.http import HttpResponse from rest_framework import serializers from rest_framework.exceptions import ParseError @@ -18,8 +17,6 @@ from sentry.profiles.flamegraph import ( FlamegraphExecutor, get_chunks_from_spans_metadata, - get_profile_ids, - get_profiles_with_function, get_spans_from_group, ) from sentry.profiles.profile_chunks import get_chunk_ids @@ -71,36 +68,6 @@ def get(self, request: Request, organization: Organization) -> HttpResponse: if not features.has("organizations:profiling", organization, actor=request.user): return Response(status=404) - if not features.has( - "organizations:continuous-profiling-compat", organization, actor=request.user - ): - snuba_params = self.get_snuba_params(request, organization) - - project_ids = snuba_params.project_ids - if len(project_ids) > 1: - raise ParseError(detail="You cannot get a flamegraph from multiple projects.") - - if request.query_params.get("fingerprint"): - sentry_sdk.set_tag("data source", "functions") - function_fingerprint = int(request.query_params["fingerprint"]) - - profile_ids = get_profiles_with_function( - organization.id, - project_ids[0], - function_fingerprint, - snuba_params, - request.GET.get("query", ""), - ) - else: - sentry_sdk.set_tag("data source", "profiles") - profile_ids = get_profile_ids(snuba_params, request.query_params.get("query", None)) - - return proxy_profiling_service( - method="POST", - path=f"/organizations/{organization.id}/projects/{project_ids[0]}/flamegraph", - json_data=profile_ids, - ) - try: snuba_params = self.get_snuba_params(request, organization) except NoProjects: diff --git a/src/sentry/features/temporary.py b/src/sentry/features/temporary.py index dfc2b17b94394d..df2cf8743fb9d5 100644 --- a/src/sentry/features/temporary.py +++ b/src/sentry/features/temporary.py @@ -80,12 +80,8 @@ def register_temporary_features(manager: FeatureManager): manager.add("organizations:continuous-profiling-beta", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Enable stopping the ingestion of continuous profile for non-beta orgs manager.add("organizations:continuous-profiling-beta-ingest", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) - # Enable continuous profiling ui - manager.add("organizations:continuous-profiling-ui", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Display profile durations on the stats page manager.add("organizations:continuous-profiling-stats", OrganizationFeature, FeatureHandlerStrategy.INTERNAL, api_expose=True) - # Enable the continuous profiling compatible redesign - manager.add("organizations:continuous-profiling-compat", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) # Delightful Developer Metrics (DDM): # Enables experimental WIP custom metrics related features manager.add("organizations:custom-metrics-experimental", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True) diff --git a/src/sentry/profiles/flamegraph.py b/src/sentry/profiles/flamegraph.py index 1a46b8ea67df72..6dc8dd8842c2e2 100644 --- a/src/sentry/profiles/flamegraph.py +++ b/src/sentry/profiles/flamegraph.py @@ -26,7 +26,6 @@ from sentry.search.events.builder.profile_functions import ProfileFunctionsQueryBuilder from sentry.search.events.fields import resolve_datetime64 from sentry.search.events.types import QueryBuilderConfig, SnubaParams -from sentry.snuba import functions from sentry.snuba.dataset import Dataset, EntityKey, StorageKey from sentry.snuba.referrer import Referrer from sentry.utils.iterators import chunked @@ -42,70 +41,6 @@ class ProfileIds(TypedDict): profile_ids: list[str] -def get_profile_ids( - snuba_params: SnubaParams, - query: str | None = None, -) -> ProfileIds: - builder = DiscoverQueryBuilder( - dataset=Dataset.Discover, - params={}, - snuba_params=snuba_params, - query=query, - selected_columns=["profile.id"], - limit=options.get("profiling.flamegraph.profile-set.size"), - ) - - builder.add_conditions( - [ - Condition(Column("type"), Op.EQ, "transaction"), - Condition(Column("profile_id"), Op.IS_NOT_NULL), - ] - ) - - result = builder.run_query(Referrer.API_PROFILING_PROFILE_FLAMEGRAPH.value) - - return {"profile_ids": [row["profile.id"] for row in result["data"]]} - - -def get_profiles_with_function( - organization_id: int, - project_id: int, - function_fingerprint: int, - snuba_params: SnubaParams, - query: str, -) -> ProfileIds: - conditions = [query, f"fingerprint:{function_fingerprint}"] - - result = functions.query( - selected_columns=["timestamp", "unique_examples()"], - query=" ".join(cond for cond in conditions if cond), - snuba_params=snuba_params, - limit=100, - orderby=["-timestamp"], - referrer=Referrer.API_PROFILING_FUNCTION_SCOPED_FLAMEGRAPH.value, - auto_aggregations=True, - use_aggregate_conditions=True, - transform_alias_to_input_format=True, - ) - - def extract_profile_ids() -> list[str]: - max_profiles = options.get("profiling.flamegraph.profile-set.size") - profile_ids = [] - - for i in range(5): - for row in result["data"]: - examples = row["unique_examples()"] - if i < len(examples): - profile_ids.append(examples[i]) - - if len(profile_ids) >= max_profiles: - return profile_ids - - return profile_ids - - return {"profile_ids": extract_profile_ids()} - - class IntervalMetadata(TypedDict): start: str end: str diff --git a/src/sentry/snuba/referrer.py b/src/sentry/snuba/referrer.py index f1888419aab37b..b19de6e0795b07 100644 --- a/src/sentry/snuba/referrer.py +++ b/src/sentry/snuba/referrer.py @@ -406,7 +406,6 @@ class Referrer(Enum): API_PROFILING_PROFILE_SUMMARY_TOTALS = "api.profiling.profile-summary-totals" API_PROFILING_PROFILE_SUMMARY_TABLE = "api.profiling.profile-summary-table" API_PROFILING_PROFILE_SUMMARY_FUNCTIONS_TABLE = "api.profiling.profile-summary-functions-table" - API_PROFILING_PROFILE_FLAMEGRAPH = "api.profiling.profile-flamegraph" API_PROFILING_PROFILE_FLAMEGRAPH_TRANSACTION_CANDIDATES = ( "api.profiling.profile-flamegraph-transaction-candidates" ) diff --git a/tests/sentry/api/endpoints/test_organization_profiling_profiles.py b/tests/sentry/api/endpoints/test_organization_profiling_profiles.py index 9ff54cd760202a..16a5863d91bcbe 100644 --- a/tests/sentry/api/endpoints/test_organization_profiling_profiles.py +++ b/tests/sentry/api/endpoints/test_organization_profiling_profiles.py @@ -16,105 +16,10 @@ from sentry.utils.snuba import bulk_snuba_queries, raw_snql_query -class OrganizationProfilingFlamegraphTestLegacy(APITestCase): - endpoint = "sentry-api-0-organization-profiling-flamegraph" - features = {"organizations:profiling": True} - - def setUp(self): - self.login_as(user=self.user) - self.url = reverse(self.endpoint, args=(self.organization.slug,)) - - def do_request(self, query, features=None, compat=True, **kwargs): - if features is None: - features = self.features - with self.feature(features): - return self.client.get( - self.url, - query, - format="json", - **kwargs, - ) - - def test_more_than_one_project(self): - projects = [ - self.create_project(), - self.create_project(), - ] - # Need this feature so we don't get the multiple project without global view error - with self.feature("organizations:global-views"): - response = self.do_request( - { - "projects": [p.id for p in projects], - } - ) - assert response.status_code == 400, response.data - assert response.data == { - "detail": ErrorDetail( - "You cannot get a flamegraph from multiple projects.", - code="parse_error", - ), - } - - @patch("sentry.search.events.builder.base.raw_snql_query", wraps=raw_snql_query) - @patch("sentry.api.endpoints.organization_profiling_profiles.proxy_profiling_service") - def test_queries_functions(self, mock_proxy_profiling_service, mock_raw_snql_query): - mock_proxy_profiling_service.return_value = HttpResponse(status=200) - - fingerprint = int(uuid4().hex[:8], 16) - - response = self.do_request( - { - "project": [self.project.id], - "query": "transaction:foo", - "fingerprint": str(fingerprint), - }, - ) - assert response.status_code == 200, response.content - - mock_raw_snql_query.assert_called_once() - - call_args = mock_raw_snql_query.call_args.args - snql_request = call_args[0] - - assert snql_request.dataset == Dataset.Functions.value - assert ( - Condition( - Function("toUInt32", [Column("fingerprint")], "fingerprint"), - Op.EQ, - fingerprint, - ) - in snql_request.query.where - ) - assert Condition(Column("transaction_name"), Op.EQ, "foo") in snql_request.query.where - - @patch("sentry.search.events.builder.base.raw_snql_query", wraps=raw_snql_query) - @patch("sentry.api.endpoints.organization_profiling_profiles.proxy_profiling_service") - def test_queries_transactions(self, mock_proxy_profiling_service, mock_raw_snql_query): - mock_proxy_profiling_service.return_value = HttpResponse(status=200) - - response = self.do_request( - { - "project": [self.project.id], - "query": "transaction:foo", - }, - ) - assert response.status_code == 200, response.content - - mock_raw_snql_query.assert_called_once() - - call_args = mock_raw_snql_query.call_args.args - snql_request = call_args[0] - - assert snql_request.dataset == Dataset.Discover.value - assert Condition(Column("profile_id"), Op.IS_NOT_NULL) in snql_request.query.where - assert Condition(Column("transaction"), Op.EQ, "foo") in snql_request.query.where - - class OrganizationProfilingFlamegraphTest(ProfilesSnubaTestCase): endpoint = "sentry-api-0-organization-profiling-flamegraph" features = { "organizations:profiling": True, - "organizations:continuous-profiling-compat": True, } def setUp(self): diff --git a/tests/sentry/profiles/test_flamegraph.py b/tests/sentry/profiles/test_flamegraph.py deleted file mode 100644 index 7720854bc6a081..00000000000000 --- a/tests/sentry/profiles/test_flamegraph.py +++ /dev/null @@ -1,111 +0,0 @@ -from datetime import timedelta, timezone - -from sentry.profiles.flamegraph import get_profiles_with_function -from sentry.search.events.types import SnubaParams -from sentry.testutils.cases import ProfilesSnubaTestCase -from sentry.testutils.helpers.datetime import before_now -from sentry.utils.samples import load_data - - -class GetProfileWithFunctionTest(ProfilesSnubaTestCase): - def setUp(self): - super().setUp() - - self.now = before_now(minutes=10) - self.hour_ago = (self.now - timedelta(hours=1)).replace( - minute=0, second=0, microsecond=0, tzinfo=timezone.utc - ) - - for i in range(3): - self.store_functions( - [ - { - "self_times_ns": [100 for _ in range(10)], - "package": "foo", - "function": "foo", - "in_app": True, - }, - ], - project=self.project, - timestamp=self.hour_ago - timedelta(hours=i), - ) - - transaction = load_data("transaction", timestamp=before_now(minutes=10)) - transaction["transaction"] = "foobar" - - self.store_functions( - [ - { - "self_times_ns": [100 for _ in range(10)], - "package": "foo", - "function": "foo", - "in_app": True, - }, - ], - project=self.project, - timestamp=self.hour_ago, - transaction=transaction, - ) - - transaction = load_data("transaction", timestamp=before_now(minutes=10)) - transaction["transaction"] = "foobar" - profile_context = transaction.setdefault("contexts", {}).setdefault("profile", {}) - profile_context["profile_id"] = "00000000000000000000000000000000" - self.store_functions( - [ - { - "self_times_ns": [100 for _ in range(10)], - "package": "foo", - "function": "foo", - "in_app": True, - }, - ], - project=self.project, - timestamp=self.hour_ago, - transaction=transaction, - ) - - def test_get_profile_with_function(self): - profile_ids = get_profiles_with_function( - self.organization.id, - self.project.id, - self.function_fingerprint({"package": "foo", "function": "foo"}), - SnubaParams( - organization=self.organization, - projects=[self.project], - start=before_now(days=1), - end=self.now, - ), - "", - ) - assert len(profile_ids["profile_ids"]) == 4, profile_ids - - def test_get_profile_with_function_with_transaction_filter(self): - profile_ids = get_profiles_with_function( - self.organization.id, - self.project.id, - self.function_fingerprint({"package": "foo", "function": "foo"}), - SnubaParams( - organization=self.organization, - projects=[self.project], - start=before_now(days=1), - end=self.now, - ), - "transaction:foobar", - ) - assert len(profile_ids["profile_ids"]) == 1, profile_ids - - def test_get_profile_with_function_no_match(self): - profile_ids = get_profiles_with_function( - self.organization.id, - self.project.id, - self.function_fingerprint({"package": "foo", "function": "foo"}), - SnubaParams( - organization=self.organization, - projects=[self.project], - start=before_now(days=1), - end=self.now, - ), - "transaction:foo", - ) - assert len(profile_ids["profile_ids"]) == 0, profile_ids