Skip to content

ref(resolvers): inline the time series resolver - #8453

Open
MeredithAnya wants to merge 1 commit into
meredith/convert-resolver-statsfrom
meredith/convert-resolver-time-series
Open

ref(resolvers): inline the time series resolver#8453
MeredithAnya wants to merge 1 commit into
meredith/convert-resolver-statsfrom
meredith/convert-resolver-time-series

Conversation

@MeredithAnya

Copy link
Copy Markdown
Member

Stack

  1. ref(resolvers): remove unused resolver declarations #8451 ref(resolvers): remove unused resolver declarations
  2. ref(resolvers): inline the trace item stats resolver #8452 ref(resolvers): inline the trace item stats resolver
  3. → #this ref(resolvers): inline the time series resolver
  4. ref(resolvers): inline the trace item table resolver
  5. ref(resolvers): remove the TraceItemDataResolver abstraction

What

Same transformation as #8452, applied to EndpointTimeSeries. ResolverTimeSeriesEAPItems was the only implementation of ResolverTimeSeries, so the trace_item_type dispatch could only ever return it.

Moves the resolver module body into endpoint_time_series.py, turns resolve() into _query_time_series reading self.routing_decision.

One thing to look at

The two modules disagreed on the name Expression: the endpoint imported the proto one from endpoint_time_series_pb2, the resolver imported the snuba AST one under that name and the proto one as ProtoExpression. The merged module keeps the resolver's imports, so _convert_aggregations_to_expressions now constructs ProtoExpression(...) instead of Expression(...). Same object, no behavior change.

Tests

Import/patch-target updates only. pytest tests/web/rpc/v1/test_endpoint_time_series/ tests/web/rpc/v1/test_storage_routing.py → 78 passed, 1 xfailed. mypy snuba/web/rpc/ clean.

🤖 Generated with Claude Code

ResolverTimeSeriesEAPItems was the only implementation of
ResolverTimeSeries, so the trace_item_type dispatch could only ever
return it. Move the resolver module body into endpoint_time_series.py
and turn resolve() into _query_time_series, following 76d1fed.

The resolver's snuba AST Expression collides with the endpoint's proto
Expression, so _convert_aggregations_to_expressions now uses the
resolver's existing ProtoExpression alias.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment on lines 584 to +592
in_msg_wrapper = TimeSeriesRequestWrapper(in_msg)
in_msg_wrapper.accept(aggregation_to_conditional_aggregation_visitor)
preprocess_expression_labels(in_msg)
resolver = self.get_resolver(in_msg.meta.trace_item_type)
return resolver.resolve(in_msg, self.routing_decision)
return self._query_time_series(in_msg)

def _query_time_series(self, in_msg: TimeSeriesRequest) -> TimeSeriesResponse:
routing_decision = self.routing_decision

# aggregations field is deprecated, it gets converted to request.expressions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The duplicate label check in _enforce_no_duplicate_labels only validates request.aggregations, allowing duplicate labels in request.expressions to be processed.
Severity: MEDIUM

Suggested Fix

Update _enforce_no_duplicate_labels to check for duplicate labels across both request.aggregations and request.expressions. The function should gather all labels from both fields to ensure the combined set is unique before any conversion or further processing occurs.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: snuba/web/rpc/v1/endpoint_time_series.py#L584-L592

Potential issue: The function `_enforce_no_duplicate_labels` only checks for duplicate
labels in `request.aggregations`, but not in `request.expressions`. This allows a
request to be processed with duplicate labels if they are sent directly in the
`expressions` field, or if labels in `aggregations` overlap with labels already present
in `expressions`. After `_convert_aggregations_to_expressions` runs, the `expressions`
list will contain these duplicates. This can lead to incorrect query results or
behavior, as downstream code like `_convert_result_timeseries` uses `expr.label` as
dictionary keys, assuming they are unique.

Did we get this right? 👍 / 👎 to inform future reviews.

@MeredithAnya
MeredithAnya added this pull request to stack #8456 September 9, 2026 23:52
Comment on lines +83 to +88
OP_TO_EXPR = {
ProtoExpression.BinaryFormula.OP_ADD: f.plus,
ProtoExpression.BinaryFormula.OP_SUBTRACT: f.minus,
ProtoExpression.BinaryFormula.OP_MULTIPLY: f.multiply,
ProtoExpression.BinaryFormula.OP_DIVIDE: f.divide,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move this elsewhere to a common file?

This is probably something we want to make sure is the same constant used everywhere, and storing it inside an endpoint file discourages that usage and hampers discoverability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants