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
13 changes: 2 additions & 11 deletions src/sentry/search/eap/trace_metrics/formulas.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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())

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
Expand All @@ -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]
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand All @@ -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)"],
Expand All @@ -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(
Expand All @@ -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,
}
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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)
Loading