Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 0 additions & 33 deletions src/sentry/api/endpoints/organization_profiling_profiles.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sentry_sdk
from django.http import HttpResponse
from rest_framework import serializers
from rest_framework.exceptions import ParseError
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down
4 changes: 0 additions & 4 deletions src/sentry/features/temporary.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
65 changes: 0 additions & 65 deletions src/sentry/profiles/flamegraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion src/sentry/snuba/referrer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
111 changes: 0 additions & 111 deletions tests/sentry/profiles/test_flamegraph.py

This file was deleted.

Loading