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
6 changes: 6 additions & 0 deletions proto/sentry_protos/snuba/v1/endpoint_time_series.proto
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ message TimeSeriesRequest {
// ex: span.environment might give 3 timeseries lines,
// one for prod, one for dev etc
repeated AttributeKey group_by = 5;

// A list of filters applied to each item type provided. These filters will be applied on a trace level to find traces
// that contain each of the provided items with matching conditions. The overall request will then only apply on those traces.
// If specified, the endpoint will only consider traces that match all the filters.
// ex: Find the number of spans in traces containing a span with op = 'db' that also contain errors with message = 'timeout'
repeated TraceItemFilterWithType trace_filters = 7;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm confused how this is supposed to work. can you include an example query for how this endpoint is supposed to function now that there are two types of filters here?

Copy link
Copy Markdown
Contributor Author

@davidtsuk davidtsuk Aug 5, 2025

Choose a reason for hiding this comment

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

it works exactly the same way GetTraces works, but I can add a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would like to see an example (aka a test) such that as a product developer I can see what the intended usage is. A few things I'm confused about:

  1. will trace_filters replace filter? seems like trace_filters are a superset of filter functionality
  2. as a user of this interface I see "enables cross-item queries` that doesn't really mean anything to me, what does it mean for me the user?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have added a test.

  1. trace_filters will not replace filter as they are responsible for different filters. First, trace_filters will filter out traces by only getting traces that contain items matching the conditions. Then, the regular trace item table or timeseries query will run with the additional condition that only those traces returned by the first query will be considered. In other words, trace_filters are applied on a trace level to filter out traces and filter is applied on the actual items in the traces.
  2. Good point, I have updated the description to explain what the functionality actually is

}

message Expression {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ message TraceItemTableRequest {

// optional, filter out results of aggregates, same as SQL HAVING
AggregationFilter aggregation_filter = 9;

// A list of filters applied to each item type provided. These filters will be applied on a trace level to find traces
// that contain each of the provided items with matching conditions. The overall request will then only apply on those traces.
// If specified, the endpoint will only consider traces that match all the filters.
// ex: Find spans in traces containing a span with op = 'db' that also contain errors with message = 'timeout'
repeated TraceItemFilterWithType trace_filters = 10;
}

message AggregationAndFilter {
Expand Down
5 changes: 5 additions & 0 deletions proto/sentry_protos/snuba/v1/request_common.proto
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ enum TraceItemType {
TRACE_ITEM_TYPE_REPLAY = 6;
}

message TraceItemFilterWithType {
TraceItemType item_type = 1;
TraceItemFilter filter = 2;
}

message PageToken {
oneof value {
// standard limit/offset pagination
Expand Down
56 changes: 56 additions & 0 deletions py/tests/test_snuba_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from sentry_protos.snuba.v1.request_common_pb2 import (
RequestMeta,
PageToken,
TraceItemFilterWithType,
TraceItemType,
TraceItemName
)
Expand Down Expand Up @@ -719,3 +720,58 @@ def test_example_attribute_names_request() -> None:
response = TraceItemAttributeNamesResponse(
attributes=[TraceItemAttributeNamesResponse.Attribute(name="foo", type=AttributeKey.Type.TYPE_STRING)]
)


def test_example_time_series_cross_item_query() -> None:
"""
Find the number of spans with http.client over time in traces containing a span with op = 'db' that also contain errors with message = 'timeout'
"""
TimeSeriesRequest(
meta=COMMON_META,
expressions=[
Expression(
aggregation=AttributeAggregation(
aggregate=Function.FUNCTION_COUNT,
key=AttributeKey(type=AttributeKey.TYPE_INT, name="span.duration"),
),
),
],
filter=TraceItemFilter(
comparison_filter=ComparisonFilter(
key=AttributeKey(
type=AttributeKey.TYPE_STRING,
name="span.op",
),
op=ComparisonFilter.OP_EQUALS,
value=AttributeValue(val_str="http.client"),
),
),
trace_filters=[
TraceItemFilterWithType(
item_type=TraceItemType.TRACE_ITEM_TYPE_SPAN,
filter=TraceItemFilter(
comparison_filter=ComparisonFilter(
key=AttributeKey(
type=AttributeKey.TYPE_STRING,
name="span.op",
),
op=ComparisonFilter.OP_EQUALS,
value=AttributeValue(val_str="db"),
),
),
),
TraceItemFilterWithType(
item_type=TraceItemType.TRACE_ITEM_TYPE_ERROR,
filter=TraceItemFilter(
comparison_filter=ComparisonFilter(
key=AttributeKey(
type=AttributeKey.TYPE_STRING,
name="error.message",
),
op=ComparisonFilter.OP_EQUALS,
value=AttributeValue(val_str="timeout"),
),
),
),
],
)
24 changes: 24 additions & 0 deletions rust/src/sentry_protos.snuba.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ pub struct AttributeAggregation {
pub enum Function {
Unspecified = 0,
Sum = 1,
/// deprecated, use FUNCTION_AVG instead
Average = 2,
Count = 3,
P50 = 4,
Expand Down Expand Up @@ -609,6 +610,13 @@ pub struct ResponseMeta {
pub downsampled_storage_meta: ::core::option::Option<DownsampledStorageMeta>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TraceItemFilterWithType {
#[prost(enumeration = "TraceItemType", tag = "1")]
pub item_type: i32,
#[prost(message, optional, tag = "2")]
pub filter: ::core::option::Option<TraceItemFilter>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PageToken {
#[prost(oneof = "page_token::Value", tags = "1, 2")]
pub value: ::core::option::Option<page_token::Value>,
Expand Down Expand Up @@ -792,6 +800,12 @@ pub struct TimeSeriesRequest {
/// one for prod, one for dev etc
#[prost(message, repeated, tag = "5")]
pub group_by: ::prost::alloc::vec::Vec<AttributeKey>,
/// A list of filters applied to each item type provided. These filters will be applied on a trace level to find traces
/// that contain each of the provided items with matching conditions. The overall request will then only apply on those traces.
/// If specified, the endpoint will only consider traces that match all the filters.
/// ex: Find the number of spans in traces containing a span with op = 'db' that also contain errors with message = 'timeout'
#[prost(message, repeated, tag = "7")]
pub trace_filters: ::prost::alloc::vec::Vec<TraceItemFilterWithType>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Expression {
Expand Down Expand Up @@ -1587,6 +1601,12 @@ pub struct TraceItemTableRequest {
/// optional, filter out results of aggregates, same as SQL HAVING
#[prost(message, optional, tag = "9")]
pub aggregation_filter: ::core::option::Option<AggregationFilter>,
/// A list of filters applied to each item type provided. These filters will be applied on a trace level to find traces
/// that contain each of the provided items with matching conditions. The overall request will then only apply on those traces.
/// If specified, the endpoint will only consider traces that match all the filters.
/// ex: Find spans in traces containing a span with op = 'db' that also contain errors with message = 'timeout'
#[prost(message, repeated, tag = "10")]
pub trace_filters: ::prost::alloc::vec::Vec<TraceItemFilterWithType>,
}
/// Nested message and enum types in `TraceItemTableRequest`.
pub mod trace_item_table_request {
Expand Down Expand Up @@ -1626,6 +1646,8 @@ pub struct AggregationComparisonFilter {
pub aggregation: ::core::option::Option<AttributeAggregation>,
#[prost(message, optional, tag = "6")]
pub conditional_aggregation: ::core::option::Option<AttributeConditionalAggregation>,
#[prost(message, optional, tag = "7")]
pub formula: ::core::option::Option<column::BinaryFormula>,
}
/// Nested message and enum types in `AggregationComparisonFilter`.
pub mod aggregation_comparison_filter {
Expand Down Expand Up @@ -1913,4 +1935,6 @@ pub struct TraceItem {
pub retention_days: u32,
#[prost(message, optional, tag = "101")]
pub received: ::core::option::Option<::prost_types::Timestamp>,
#[prost(uint32, tag = "102")]
pub downsampled_retention_days: u32,
}
Loading