22from collections import defaultdict
33from dataclasses import replace
44from datetime import datetime
5- from typing import Any , Callable , Dict , Iterable
5+ from typing import Any , Callable , Dict , Iterable , Optional
66
77import sentry_sdk
88from google .protobuf .json_format import MessageToDict
3030from snuba .query import OrderBy , OrderByDirection , SelectedExpression
3131from snuba .query .data_source .simple import Entity
3232from snuba .query .dsl import Functions as f
33- from snuba .query .dsl import column , literal
33+ from snuba .query .dsl import column , in_cond , literal , literals_array
3434from snuba .query .expressions import Expression
3535from snuba .query .logical import Query
3636from snuba .query .query_settings import HTTPQuerySettings
3737from snuba .request import Request as SnubaRequest
3838from snuba .settings import ENABLE_FORMULA_RELIABILITY_DEFAULT
3939from snuba .state import get_int_config
40+ from snuba .utils .metrics .timer import Timer
4041from snuba .web .query import run_query
4142from snuba .web .rpc .common .common import (
4243 add_existence_check_to_subscriptable_references ,
6162 get_confidence_interval_column ,
6263 get_count_column ,
6364)
65+ from snuba .web .rpc .v1 .resolvers .common .cross_item_queries import (
66+ get_trace_ids_for_cross_item_query ,
67+ )
6468from snuba .web .rpc .v1 .resolvers .common .formula_reliability import (
6569 FormulaReliabilityCalculator ,
6670)
@@ -141,13 +145,11 @@ def _convert_result_timeseries(
141145 # time_converted_to_integer_timestamp: row_data_for_that_time_bucket
142146 # }
143147 # }
144- result_timeseries_timestamp_to_row : defaultdict [
145- tuple [str , str ], dict [int , Dict [str , Any ]]
146- ] = defaultdict (dict )
147-
148- query_duration = (
149- request .meta .end_timestamp .seconds - request .meta .start_timestamp .seconds
148+ result_timeseries_timestamp_to_row : defaultdict [tuple [str , str ], dict [int , Dict [str , Any ]]] = (
149+ defaultdict (dict )
150150 )
151+
152+ query_duration = request .meta .end_timestamp .seconds - request .meta .start_timestamp .seconds
151153 time_buckets = [
152154 Timestamp (seconds = (request .meta .start_timestamp .seconds ) + secs )
153155 for secs in range (0 , query_duration , request .granularity_secs )
@@ -183,9 +185,7 @@ def _convert_result_timeseries(
183185 if not row_data :
184186 timeseries .data_points .append (DataPoint (data = 0 , data_present = False ))
185187 else :
186- extrapolation_context = ExtrapolationContext .from_row (
187- timeseries .label , row_data
188- )
188+ extrapolation_context = ExtrapolationContext .from_row (timeseries .label , row_data )
189189 if row_data .get (timeseries .label , None ) is not None :
190190 timeseries .data_points .append (
191191 DataPoint (
@@ -199,9 +199,7 @@ def _convert_result_timeseries(
199199 else :
200200 timeseries .data_points .append (DataPoint (data = 0 , data_present = False ))
201201
202- if get_int_config (
203- "enable_formula_reliability_ts" , ENABLE_FORMULA_RELIABILITY_DEFAULT
204- ):
202+ if get_int_config ("enable_formula_reliability_ts" , ENABLE_FORMULA_RELIABILITY_DEFAULT ):
205203 frc = FormulaReliabilityCalculator (request , data , time_buckets )
206204 for timeseries in result_timeseries .values ():
207205 if timeseries .label in frc :
@@ -240,10 +238,7 @@ def _get_reliability_context_columns(
240238 which_oneof = expr .WhichOneof ("expression" )
241239 assert which_oneof in ["conditional_aggregation" , "aggregation" ]
242240 aggregation = getattr (expr , which_oneof )
243- if (
244- aggregation .extrapolation_mode
245- == ExtrapolationMode .EXTRAPOLATION_MODE_SAMPLE_WEIGHTED
246- ):
241+ if aggregation .extrapolation_mode == ExtrapolationMode .EXTRAPOLATION_MODE_SAMPLE_WEIGHTED :
247242 confidence_interval_column = get_confidence_interval_column (
248243 aggregation , _get_attribute_key_to_expression_function (request_meta )
249244 )
@@ -271,9 +266,7 @@ def _get_reliability_context_columns(
271266 SelectedExpression (name = count_column .alias , expression = count_column )
272267 )
273268 elif expr .WhichOneof ("expression" ) == "formula" :
274- if not get_int_config (
275- "enable_formula_reliability_ts" , ENABLE_FORMULA_RELIABILITY_DEFAULT
276- ):
269+ if not get_int_config ("enable_formula_reliability_ts" , ENABLE_FORMULA_RELIABILITY_DEFAULT ):
277270 return []
278271 # also query for the left and right parts of the formula separately
279272 # this will be used later to calculate the reliability of the formula
@@ -286,9 +279,7 @@ def _get_reliability_context_columns(
286279 expression = _proto_expression_to_ast_expression (e , request_meta ),
287280 )
288281 )
289- additional_context_columns .extend (
290- _get_reliability_context_columns (e , request_meta )
291- )
282+ additional_context_columns .extend (_get_reliability_context_columns (e , request_meta ))
292283 return additional_context_columns
293284
294285
@@ -311,13 +302,9 @@ def _proto_expression_to_ast_expression(
311302 case None :
312303 pass
313304 case "default_value_double" :
314- formula_expr = f .coalesce (
315- formula_expr , expr .formula .default_value_double
316- )
305+ formula_expr = f .coalesce (formula_expr , expr .formula .default_value_double )
317306 case "default_value_int64" :
318- formula_expr = f .coalesce (
319- formula_expr , expr .formula .default_value_int64
320- )
307+ formula_expr = f .coalesce (formula_expr , expr .formula .default_value_int64 )
321308 case default :
322309 raise BadSnubaRPCRequestException (
323310 f"Unknown default_value in formula. Expected default_value_double or default_value_int64 but got { default } "
@@ -329,7 +316,7 @@ def _proto_expression_to_ast_expression(
329316 raise ValueError (f"Unknown expression type: { default } " )
330317
331318
332- def build_query (request : TimeSeriesRequest ) -> Query :
319+ def build_query (request : TimeSeriesRequest , timer : Optional [ Timer ] = None ) -> Query :
333320 entity = Entity (
334321 key = EntityKey ("eap_items" ),
335322 schema = get_entity (EntityKey ("eap_items" )).get_data_model (),
@@ -346,21 +333,30 @@ def build_query(request: TimeSeriesRequest) -> Query:
346333
347334 additional_context_columns = []
348335 for expr in request .expressions :
349- additional_context_columns .extend (
350- _get_reliability_context_columns (expr , request .meta )
351- )
336+ additional_context_columns .extend (_get_reliability_context_columns (expr , request .meta ))
352337
353338 groupby_columns = [
354339 SelectedExpression (
355340 name = attr_key .name ,
356- expression = _get_attribute_key_to_expression_function (request .meta )(
357- attr_key
358- ),
341+ expression = _get_attribute_key_to_expression_function (request .meta )(attr_key ),
359342 )
360343 for attr_key in request .group_by
361344 ]
362345 item_type_conds = [f .equals (column ("item_type" ), request .meta .trace_item_type )]
363346
347+ # Handle cross item queries by first getting trace IDs
348+ additional_conditions = []
349+ if request .trace_filters and timer is not None :
350+ trace_ids = get_trace_ids_for_cross_item_query (
351+ request , request .meta , list (request .trace_filters ), timer
352+ )
353+ additional_conditions .append (
354+ in_cond (
355+ column ("trace_id" ),
356+ literals_array (None , [literal (trace_id ) for trace_id in trace_ids ]),
357+ )
358+ )
359+
364360 res = Query (
365361 from_clause = entity ,
366362 selected_columns = [
@@ -402,6 +398,7 @@ def build_query(request: TimeSeriesRequest) -> Query:
402398 request .filter , _get_attribute_key_to_expression_function (request .meta )
403399 ),
404400 * item_type_conds ,
401+ * additional_conditions ,
405402 ),
406403 groupby = [
407404 column ("time_slot" ),
@@ -410,17 +407,15 @@ def build_query(request: TimeSeriesRequest) -> Query:
410407 for attr_key in request .group_by
411408 ],
412409 ],
413- order_by = [
414- OrderBy (expression = column ("time_slot" ), direction = OrderByDirection .ASC )
415- ],
410+ order_by = [OrderBy (expression = column ("time_slot" ), direction = OrderByDirection .ASC )],
416411 )
417412 treeify_or_and_conditions (res )
418413 add_existence_check_to_subscriptable_references (res )
419414 return res
420415
421416
422417def _build_snuba_request (
423- request : TimeSeriesRequest , query_settings : HTTPQuerySettings
418+ request : TimeSeriesRequest , query_settings : HTTPQuerySettings , timer : Optional [ Timer ] = None
424419) -> SnubaRequest :
425420 if request .meta .trace_item_type == TraceItemType .TRACE_ITEM_TYPE_LOG :
426421 team = "ourlogs"
@@ -434,7 +429,7 @@ def _build_snuba_request(
434429 return SnubaRequest (
435430 id = uuid .UUID (request .meta .request_id ),
436431 original_body = MessageToDict (request ),
437- query = build_query (request ),
432+ query = build_query (request , timer ),
438433 query_settings = query_settings ,
439434 attribution_info = AttributionInfo (
440435 referrer = request .meta .referrer ,
@@ -464,18 +459,14 @@ def resolve(
464459 # if the user passes it in
465460 assert len (in_msg .aggregations ) == 0
466461
467- query_settings = (
468- setup_trace_query_settings () if in_msg .meta .debug else HTTPQuerySettings ()
469- )
462+ query_settings = setup_trace_query_settings () if in_msg .meta .debug else HTTPQuerySettings ()
470463 try :
471- routing_decision .strategy .merge_clickhouse_settings (
472- routing_decision , query_settings
473- )
464+ routing_decision .strategy .merge_clickhouse_settings (routing_decision , query_settings )
474465 query_settings .set_sampling_tier (routing_decision .tier )
475466 except Exception as e :
476467 sentry_sdk .capture_message (f"Error merging clickhouse settings: { e } " )
477468
478- snuba_request = _build_snuba_request (in_msg , query_settings )
469+ snuba_request = _build_snuba_request (in_msg , query_settings , self . _timer )
479470 res = run_query (
480471 dataset = PluggableDataset (name = "eap" , all_entities = []),
481472 request = snuba_request ,
0 commit comments