diff --git a/src/sentry/search/eap/trace_metrics/formulas.py b/src/sentry/search/eap/trace_metrics/formulas.py index d5817414d110..fa506ce42d32 100644 --- a/src/sentry/search/eap/trace_metrics/formulas.py +++ b/src/sentry/search/eap/trace_metrics/formulas.py @@ -122,8 +122,6 @@ def per_minute(args: ResolvedArguments, settings: ResolverSettings) -> Column.Bi WEB_VITALS = ["lcp", "fcp", "cls", "ttfb", "inp"] -TOTAL_SCORE_ARGUMENT = "measurements.score.total" - WEB_VITAL_SCORE_METRICS: dict[str, TraceMetric] = { vital: TraceMetric( metric_name=f"browser.web_vital.{vital}.score", @@ -275,13 +273,6 @@ def opportunity_score(_: ResolvedArguments, settings: ResolverSettings) -> Colum return operate_multiple_columns(weighted_scores, Column.BinaryFormula.OP_ADD) -def _total_score_argument() -> ValueArgumentDefinition: - return ValueArgumentDefinition( - argument_types={"string"}, - validator=literal_validator([TOTAL_SCORE_ARGUMENT]), - ) - - def _score_metrics(_: ResolvedArguments) -> frozenset[TraceMetric]: return frozenset(WEB_VITAL_SCORE_METRICS.values()) @@ -391,7 +382,7 @@ def if_combinator(formula: FormulaDefinition) -> FormulaDefinition: "performance_score": MultiTraceMetricFormulaDefinition( default_search_type="percentage", infer_search_type_from_arguments=False, - arguments=[_total_score_argument()], + arguments=[], formula_resolver=performance_score, trace_metrics_resolver=_score_metrics, processor=none_if_zero_processor, @@ -401,7 +392,7 @@ def if_combinator(formula: FormulaDefinition) -> FormulaDefinition: "opportunity_score": MultiTraceMetricFormulaDefinition( default_search_type="percentage", infer_search_type_from_arguments=False, - arguments=[_total_score_argument()], + arguments=[], formula_resolver=opportunity_score, trace_metrics_resolver=_score_metrics, is_aggregate=True, diff --git a/tests/snuba/api/endpoints/test_organization_events_timeseries_trace_metrics.py b/tests/snuba/api/endpoints/test_organization_events_timeseries_trace_metrics.py index 23eae0146f7e..9530c6af9b7b 100644 --- a/tests/snuba/api/endpoints/test_organization_events_timeseries_trace_metrics.py +++ b/tests/snuba/api/endpoints/test_organization_events_timeseries_trace_metrics.py @@ -555,7 +555,7 @@ def test_web_vital_total_performance_score(self) -> None: "start": self.start, "end": self.end, "interval": "1h", - "yAxis": "performance_score(measurements.score.total)", + "yAxis": "performance_score()", "project": self.project.id, "dataset": "tracemetrics", }, @@ -564,7 +564,7 @@ def test_web_vital_total_performance_score(self) -> None: assert response.status_code == 200, response.content assert len(response.data["timeSeries"]) == 1 timeseries = response.data["timeSeries"][0] - assert timeseries["yAxis"] == "performance_score(measurements.score.total)" + assert timeseries["yAxis"] == "performance_score()" # only the vitals with data in each bucket contribute their weight (LCP 0.30, CLS 0.15) assert [bucket["value"] for bucket in timeseries["values"]] == pytest.approx( [(1.0 * 0.30 + 0.5 * 0.15) / 0.45, 0.5, 1.0] @@ -593,9 +593,9 @@ def test_web_vital_total_opportunity_score_top_events(self) -> None: "start": self.start, "end": self.end, "interval": "1h", - "yAxis": "opportunity_score(measurements.score.total)", + "yAxis": "opportunity_score()", "groupBy": ["transaction"], - "orderby": "-opportunity_score(measurements.score.total)", + "orderby": "-opportunity_score()", "project": self.project.id, "dataset": "tracemetrics", "excludeOther": 0, diff --git a/tests/snuba/api/endpoints/test_organization_events_trace_metrics.py b/tests/snuba/api/endpoints/test_organization_events_trace_metrics.py index 16d4ad0dfb3e..bd29c83ba884 100644 --- a/tests/snuba/api/endpoints/test_organization_events_trace_metrics.py +++ b/tests/snuba/api/endpoints/test_organization_events_trace_metrics.py @@ -1042,7 +1042,7 @@ def test_web_vital_total_performance_score(self) -> None: response = self.do_request( { - "field": ["performance_score(measurements.score.total)"], + "field": ["performance_score()"], "project": self.project.id, "dataset": self.dataset, } @@ -1054,10 +1054,13 @@ def test_web_vital_total_performance_score(self) -> None: assert len(data) == 1 # only LCP (weight 0.30) and CLS (weight 0.15) have data, so the other weights drop out expected = (1.0 * 0.30 + 0.5 * 0.15) / (0.30 + 0.15) - assert data[0]["performance_score(measurements.score.total)"] == pytest.approx(expected) - assert meta["fields"]["performance_score(measurements.score.total)"] == "percentage" + assert data[0]["performance_score()"] == pytest.approx(expected) + assert meta["fields"]["performance_score()"] == "percentage" + + def test_web_vital_total_performance_score_legacy_argument(self) -> None: + # Queries saved before the argument was dropped still resolve, and the argument is ignored + self.store_eap_items([self._create_web_vital_score("lcp", 0.5)]) - def test_web_vital_total_performance_score_without_data(self) -> None: response = self.do_request( { "field": ["performance_score(measurements.score.total)"], @@ -1069,7 +1072,21 @@ def test_web_vital_total_performance_score_without_data(self) -> None: assert response.status_code == 200, response.content data = response.data["data"] assert len(data) == 1 - assert data[0]["performance_score(measurements.score.total)"] is None + assert data[0]["performance_score(measurements.score.total)"] == pytest.approx(0.5) + + def test_web_vital_total_performance_score_without_data(self) -> None: + response = self.do_request( + { + "field": ["performance_score()"], + "project": self.project.id, + "dataset": self.dataset, + } + ) + + assert response.status_code == 200, response.content + data = response.data["data"] + assert len(data) == 1 + assert data[0]["performance_score()"] is None def test_web_vital_total_opportunity_score(self) -> None: self.store_eap_items( @@ -1082,8 +1099,8 @@ def test_web_vital_total_opportunity_score(self) -> None: response = self.do_request( { - "field": ["transaction", "opportunity_score(measurements.score.total)"], - "orderby": "-opportunity_score(measurements.score.total)", + "field": ["transaction", "opportunity_score()"], + "orderby": "-opportunity_score()", "project": self.project.id, "dataset": self.dataset, } @@ -1095,9 +1112,9 @@ def test_web_vital_total_opportunity_score(self) -> None: assert data[0]["transaction"] == "/slow" # (2 scores - 1.0 total ratio) / 3 scores overall; LCP is the only vital with data, so # it carries the full weight - assert data[0]["opportunity_score(measurements.score.total)"] == pytest.approx(1 / 3) + assert data[0]["opportunity_score()"] == pytest.approx(1 / 3) assert data[1]["transaction"] == "/fast" - assert data[1]["opportunity_score(measurements.score.total)"] == pytest.approx(0) + assert data[1]["opportunity_score()"] == pytest.approx(0) def test_web_vital_scores_alongside_metric_aggregates(self) -> None: # Regression guard: the query level metric filter must include the metrics the score @@ -1115,7 +1132,7 @@ def test_web_vital_scores_alongside_metric_aggregates(self) -> None: "field": [ "p75(value,browser.web_vital.cls,distribution,-)", "avg(value,browser.web_vital.lcp.score,distribution,ratio)", - "performance_score(measurements.score.total)", + "performance_score()", ], "project": self.project.id, "dataset": self.dataset, @@ -1130,4 +1147,4 @@ def test_web_vital_scores_alongside_metric_aggregates(self) -> None: "avg(value,browser.web_vital.lcp.score,distribution,ratio)" ] == pytest.approx(0.5) expected = (0.5 * 0.30 + 0.25 * 0.15) / (0.30 + 0.15) - assert data[0]["performance_score(measurements.score.total)"] == pytest.approx(expected) + assert data[0]["performance_score()"] == pytest.approx(expected)