Skip to content

Commit 4869fef

Browse files
Riccardo Busettiantonpirker
authored andcommitted
fix(ddm): Fix custom metrics querying via mqb (#60000)
1 parent 1aee420 commit 4869fef

File tree

11 files changed

+71
-142
lines changed

11 files changed

+71
-142
lines changed

src/sentry/snuba/metrics/datasource.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def get_available_derived_metrics(
131131

132132
# Initially, we need all derived metrics to be able to support derived metrics that are not
133133
# private but might have private constituent metrics
134-
all_derived_metrics = get_derived_metrics(exclude_private=False)
134+
all_derived_metrics = get_derived_metrics()
135135

136136
for derived_metric_mri, derived_metric_obj in all_derived_metrics.items():
137137
try:
@@ -159,8 +159,8 @@ def get_available_derived_metrics(
159159
if single_entity_constituents.issubset(found_derived_metrics):
160160
found_derived_metrics.add(composite_derived_metric_obj.metric_mri)
161161

162-
public_derived_metrics = set(get_derived_metrics(exclude_private=True).keys())
163-
return found_derived_metrics.intersection(public_derived_metrics)
162+
all_derived_metrics = set(get_derived_metrics().keys())
163+
return found_derived_metrics.intersection(all_derived_metrics)
164164

165165

166166
def get_metrics_meta(projects: Sequence[Project], use_case_id: UseCaseID) -> Sequence[MetricMeta]:
@@ -294,7 +294,7 @@ def _get_metrics_filter_ids(
294294
org_id = org_id_from_projects(projects)
295295

296296
metric_mris_deque = deque(metric_mris)
297-
all_derived_metrics = get_derived_metrics(exclude_private=False)
297+
all_derived_metrics = get_derived_metrics()
298298

299299
while metric_mris_deque:
300300
mri = metric_mris_deque.popleft()
@@ -329,9 +329,9 @@ def _validate_requested_derived_metrics_in_input_metrics(
329329
SingleEntityDerivedMetric was incorrectly setup with constituent metrics that span multiple
330330
entities
331331
"""
332-
public_derived_metrics = get_derived_metrics(exclude_private=True)
332+
all_derived_metrics = get_derived_metrics()
333333
requested_derived_metrics = {
334-
metric_mri for metric_mri in metric_mris if metric_mri in public_derived_metrics
334+
metric_mri for metric_mri in metric_mris if metric_mri in all_derived_metrics
335335
}
336336
found_derived_metrics = get_available_derived_metrics(
337337
projects, supported_metric_ids_in_entities, use_case_id
@@ -381,13 +381,6 @@ def _fetch_tags_or_values_for_mri(
381381
"""
382382
org_id = projects[0].organization_id
383383

384-
if metric_mris is not None:
385-
private_derived_metrics = set(get_derived_metrics(exclude_private=False).keys()) - set(
386-
get_derived_metrics(exclude_private=True).keys()
387-
)
388-
if set(metric_mris).intersection(private_derived_metrics) != set():
389-
raise InvalidParams(f"Metric MRIs {metric_mris} do not exist")
390-
391384
try:
392385
metric_ids = _get_metrics_filter_ids(
393386
projects=projects, metric_mris=metric_mris, use_case_id=use_case_id
@@ -526,9 +519,9 @@ def get_single_metric_info(
526519
}
527520

528521
metric_mri = get_mri(metric_name)
529-
public_derived_metrics = get_derived_metrics(exclude_private=True)
530-
if metric_mri in public_derived_metrics:
531-
derived_metric = public_derived_metrics[metric_mri]
522+
all_derived_metrics = get_derived_metrics()
523+
if metric_mri in all_derived_metrics:
524+
derived_metric = all_derived_metrics[metric_mri]
532525
response_dict.update(
533526
{
534527
"operations": derived_metric.generate_available_operations(),

src/sentry/snuba/metrics/fields/base.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
uniq_aggregation_on_metric,
7878
uniq_if_column_snql,
7979
)
80-
from sentry.snuba.metrics.naming_layer.mapping import get_public_name_from_mri, is_private_mri
80+
from sentry.snuba.metrics.naming_layer.mapping import get_public_name_from_mri
8181
from sentry.snuba.metrics.naming_layer.mri import SessionMRI, SpanMRI, TransactionMRI
8282
from sentry.snuba.metrics.utils import (
8383
DEFAULT_AGGREGATES,
@@ -1896,7 +1896,7 @@ def metric_object_factory(
18961896
# that no private derived metrics are required. The query builder requires access to all
18971897
# derived metrics to be able to compute derived metrics that are not private but might have
18981898
# private constituents
1899-
derived_metrics = get_derived_metrics(exclude_private=False)
1899+
derived_metrics = get_derived_metrics()
19001900
if metric_mri in derived_metrics:
19011901
return derived_metrics[metric_mri]
19021902

@@ -1929,13 +1929,5 @@ def generate_bottom_up_dependency_tree_for_metrics(
19291929
return dependency_list
19301930

19311931

1932-
def get_derived_metrics(exclude_private: bool = True) -> Mapping[str, DerivedMetricExpression]:
1933-
return (
1934-
{
1935-
mri: expression
1936-
for (mri, expression) in DERIVED_METRICS.items()
1937-
if not is_private_mri(mri)
1938-
}
1939-
if exclude_private
1940-
else DERIVED_METRICS
1941-
)
1932+
def get_derived_metrics() -> Mapping[str, DerivedMetricExpression]:
1933+
return DERIVED_METRICS

src/sentry/snuba/metrics/naming_layer/mapping.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ def get_mri(external_name: Union[Enum, str]) -> str:
7777

7878

7979
def get_public_name_from_mri(internal_name: Union[TransactionMRI, SessionMRI, str]) -> str:
80-
"""Returns the public name from a MRI if it has a mapping to a public metric name, otherwise raise an exception"""
80+
"""
81+
Returns the public name from a MRI if it has a mapping to a public metric name, otherwise return the internal
82+
name.
83+
"""
8184
if not len(MRI_TO_NAME):
8285
create_name_mapping_layers()
8386

@@ -90,15 +93,13 @@ def get_public_name_from_mri(internal_name: Union[TransactionMRI, SessionMRI, st
9093
elif (alias := _extract_name_from_custom_metric_mri(internal_name)) is not None:
9194
return alias
9295
else:
93-
raise InvalidParams(f"Unable to find a mri reverse mapping for '{internal_name}'.")
96+
return internal_name
9497

9598

9699
def is_private_mri(internal_name: Union[TransactionMRI, SessionMRI, str]) -> bool:
97-
try:
98-
get_public_name_from_mri(internal_name)
99-
return False
100-
except InvalidParams:
101-
return True
100+
public_name = get_public_name_from_mri(internal_name)
101+
# If the public name is the same as internal name it means that the internal is "private".
102+
return public_name == internal_name
102103

103104

104105
def _extract_name_from_custom_metric_mri(mri: str) -> Optional[str]:

src/sentry/snuba/metrics/query.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class MetricField:
4141
Dict[str, Union[None, str, int, float, Sequence[Tuple[Union[str, int], ...]]]]
4242
] = None
4343
alias: Optional[str] = None
44-
allow_private: bool = False
4544

4645
def __post_init__(self) -> None:
4746
# Validate that it is a valid MRI format
@@ -57,9 +56,6 @@ def __post_init__(self) -> None:
5756

5857
@property
5958
def _metric_name(self) -> str:
60-
if self.allow_private:
61-
return self.metric_mri
62-
6359
return get_public_name_from_mri(self.metric_mri)
6460

6561
def __str__(self) -> str:
@@ -193,7 +189,7 @@ def _use_case_id(metric_mri: str) -> UseCaseID:
193189

194190
@staticmethod
195191
def _validate_field(field: MetricField) -> None:
196-
derived_metrics_mri = get_derived_metrics(exclude_private=True)
192+
all_derived_metrics = get_derived_metrics()
197193

198194
# Validate the validity of the expression meaning that if an operation is present, then it needs to be one of
199195
# of the supported operations and that the metric mri should be one of the aggregated derived metrics
@@ -202,14 +198,9 @@ def _validate_field(field: MetricField) -> None:
202198
raise InvalidParams(
203199
f"Invalid operation '{field.op}'. Must be one of {', '.join(OPERATIONS)}"
204200
)
205-
if field.metric_mri in derived_metrics_mri:
206-
metric_name = (
207-
field.metric_mri
208-
if field.allow_private
209-
else get_public_name_from_mri(field.metric_mri)
210-
)
201+
if field.metric_mri in all_derived_metrics:
211202
raise DerivedMetricParseException(
212-
f"Failed to parse {field.op}({metric_name}). No operations can be "
203+
f"Failed to parse {field.op}({get_public_name_from_mri(field.metric_mri)}). No operations can be "
213204
f"applied on this field as it is already a derived metric with an "
214205
f"aggregation applied to it."
215206
)

src/sentry/snuba/metrics/query_builder.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,16 @@ def _strip_project_id(condition: Condition) -> Optional[Condition]:
110110
return None
111111

112112

113-
def parse_field(field: str, allow_mri: bool = False, allow_private: bool = False) -> MetricField:
113+
def parse_field(field: str, allow_mri: bool = False) -> MetricField:
114114
if allow_mri:
115115
mri_matches = MRI_SCHEMA_REGEX.match(field) or MRI_EXPRESSION_REGEX.match(field)
116116
if mri_matches:
117-
return parse_mri_field(field, allow_private)
117+
return parse_mri_field(field)
118118

119119
return parse_public_field(field)
120120

121121

122-
def parse_mri_field(field: str, allow_private: bool = False) -> MetricField:
122+
def parse_mri_field(field: str) -> MetricField:
123123
matches = MRI_EXPRESSION_REGEX.match(field)
124124

125125
try:
@@ -129,7 +129,7 @@ def parse_mri_field(field: str, allow_private: bool = False) -> MetricField:
129129
operation = None
130130
mri = field
131131

132-
return MetricField(op=operation, metric_mri=mri, allow_private=allow_private)
132+
return MetricField(op=operation, metric_mri=mri)
133133

134134

135135
def parse_public_field(field: str) -> MetricField:
@@ -524,7 +524,6 @@ def __init__(
524524
parse_field(
525525
key,
526526
allow_mri=allow_mri,
527-
allow_private=query_params.get("allowPrivate") == "true",
528527
)
529528
for key in query_params.getlist("field", [])
530529
]

src/sentry/snuba/metrics_enhanced_performance.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ def timeseries_query(
148148
# any remaining errors mean we should try again with discover
149149
except IncompatibleMetricsQuery as error:
150150
sentry_sdk.set_tag("performance.mep_incompatible", str(error))
151+
# We want to capture this exception to have more visibility in case the query can't be satisfied by metrics.
152+
sentry_sdk.capture_exception(error)
151153
metrics_compatible = False
152154
except Exception as error:
153155
raise error

tests/sentry/api/endpoints/test_organization_metric_data.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,6 @@ def test_gauges(self):
16301630
per_page=3,
16311631
useCase="custom",
16321632
includeSeries="1",
1633-
allowPrivate="true",
16341633
)
16351634
groups = response.data["groups"]
16361635

@@ -1639,20 +1638,20 @@ def test_gauges(self):
16391638
{
16401639
"by": {},
16411640
"series": {
1642-
"count(g:custom/page_load@millisecond)": [2, 3],
1643-
"max(g:custom/page_load@millisecond)": [20.0, 21.0],
1644-
"min(g:custom/page_load@millisecond)": [1.0, 2.0],
1645-
"last(g:custom/page_load@millisecond)": [20.0, 4.0],
1646-
"sum(g:custom/page_load@millisecond)": [21.0, 21.0],
1647-
"avg(g:custom/page_load@millisecond)": [10.5, 7.0],
1641+
"count(page_load)": [2, 3],
1642+
"max(page_load)": [20.0, 21.0],
1643+
"min(page_load)": [1.0, 2.0],
1644+
"last(page_load)": [20.0, 4.0],
1645+
"sum(page_load)": [21.0, 21.0],
1646+
"avg(page_load)": [10.5, 7.0],
16481647
},
16491648
"totals": {
1650-
"count(g:custom/page_load@millisecond)": 5,
1651-
"max(g:custom/page_load@millisecond)": 21.0,
1652-
"min(g:custom/page_load@millisecond)": 1.0,
1653-
"last(g:custom/page_load@millisecond)": 4.0,
1654-
"sum(g:custom/page_load@millisecond)": 42.0,
1655-
"avg(g:custom/page_load@millisecond)": 8.4,
1649+
"count(page_load)": 5,
1650+
"max(page_load)": 21.0,
1651+
"min(page_load)": 1.0,
1652+
"last(page_load)": 4.0,
1653+
"sum(page_load)": 42.0,
1654+
"avg(page_load)": 8.4,
16561655
},
16571656
}
16581657
]

tests/sentry/snuba/metrics/test_metrics_layer/test_metrics_enhanced_performance.py

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -411,49 +411,6 @@ def test_custom_measurement_query_with_valid_mri(self):
411411
key=lambda elem: elem["name"],
412412
)
413413

414-
def test_custom_measurement_query_with_invalid_mri(self):
415-
invalid_mris = [
416-
"d:sessions/measurements.speed@millisecond",
417-
"s:transactions/measurements.speed@millisecond",
418-
]
419-
420-
for value, invalid_mri in zip([100, 200], invalid_mris):
421-
self.store_performance_metric(
422-
name=invalid_mri,
423-
tags={},
424-
value=value,
425-
)
426-
427-
for invalid_mri in invalid_mris:
428-
with pytest.raises(
429-
InvalidParams,
430-
match=f"Unable to find a mri reverse mapping for '{invalid_mri}'.",
431-
):
432-
# We keep the query in order to add more context to the test, even though the actual test
433-
# is testing for the '__post_init__' inside 'MetricField'.
434-
metrics_query = self.build_metrics_query(
435-
before_now="1h",
436-
granularity="1h",
437-
select=[
438-
MetricField(
439-
op="count",
440-
metric_mri=invalid_mri,
441-
),
442-
],
443-
groupby=[],
444-
orderby=[],
445-
limit=Limit(limit=2),
446-
offset=Offset(offset=0),
447-
include_series=False,
448-
)
449-
450-
get_series(
451-
[self.project],
452-
metrics_query=metrics_query,
453-
include_meta=True,
454-
use_case_id=UseCaseID.TRANSACTIONS,
455-
)
456-
457414
def test_query_with_order_by_valid_str_field(self):
458415
project_2 = self.create_project()
459416
project_3 = self.create_project()

tests/sentry/snuba/metrics/test_naming_layer.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@
22

33
import pytest
44

5-
from sentry.snuba.metrics.naming_layer import create_name_mapping_layers
6-
from sentry.snuba.metrics.naming_layer.mapping import MRI_TO_NAME, is_private_mri
75
from sentry.snuba.metrics.naming_layer.mri import (
86
MRI_SCHEMA_REGEX,
97
ParsedMRI,
10-
SessionMRI,
11-
TransactionMRI,
128
is_custom_measurement,
139
parse_mri,
1410
)
@@ -144,12 +140,3 @@ def test_parse_mri(name, expected):
144140
)
145141
def test_is_custom_measurement(parsed_mri, expected):
146142
assert is_custom_measurement(parsed_mri) == expected
147-
148-
149-
@pytest.mark.parametrize("mri", (list(TransactionMRI) + list(SessionMRI)))
150-
def test_is_private_mri(mri):
151-
create_name_mapping_layers()
152-
153-
public_mris = set(MRI_TO_NAME.keys())
154-
expected_private = False if mri.value in public_mris else True
155-
assert is_private_mri(mri) == expected_private

tests/sentry/snuba/metrics/test_query.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -570,27 +570,6 @@ def test_validate_metric_field_mri():
570570
)
571571

572572

573-
@pytest.mark.parametrize(
574-
"alias",
575-
[
576-
pytest.param(
577-
None,
578-
id="No alias provided",
579-
),
580-
pytest.param(
581-
"ahmed_alias",
582-
id="alias is provided",
583-
),
584-
],
585-
)
586-
def test_validate_metric_field_mri_is_public(alias):
587-
with pytest.raises(
588-
InvalidParams,
589-
match=f"Unable to find a mri reverse mapping for '{SessionMRI.ERRORED_ALL.value}'.",
590-
):
591-
MetricField(op=None, metric_mri=SessionMRI.ERRORED_ALL.value, alias=alias)
592-
593-
594573
@pytest.mark.parametrize(
595574
"select, interval, series",
596575
[

0 commit comments

Comments
 (0)