From 1504eda6fb8ebb14379743f07d5fe4b6d10c5453 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Tue, 21 Apr 2026 16:01:35 +0200 Subject: [PATCH 1/5] fix(span): Make a top-level field --- relay-event-schema/src/protocol/span_v2.rs | 13 +++++++++++++ relay-spans/src/v1_to_v2.rs | 5 +---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/relay-event-schema/src/protocol/span_v2.rs b/relay-event-schema/src/protocol/span_v2.rs index bb77e049a14..92be9a8180e 100644 --- a/relay-event-schema/src/protocol/span_v2.rs +++ b/relay-event-schema/src/protocol/span_v2.rs @@ -50,6 +50,19 @@ pub struct SpanV2 { #[metastructure(pii = "true", trim = true)] pub attributes: Annotated, + /// Temporary flag that controls where performance issues are detected. + /// + /// When the flag is set to true, performance issues will be detected on this span provided it + /// is a root (segment) instead of the transaction event. + /// + /// Only set on root spans extracted from transactions. + #[metastructure( + field = "_performance_issues_spans", + skip_serialization = "empty", + trim = false + )] + pub performance_issues_spans: Annotated, + /// Additional arbitrary fields for forwards compatibility. #[metastructure(additional_properties, pii = "maybe")] pub other: Object, diff --git a/relay-spans/src/v1_to_v2.rs b/relay-spans/src/v1_to_v2.rs index 39a82e9ed89..f6b7911e4e6 100644 --- a/relay-spans/src/v1_to_v2.rs +++ b/relay-spans/src/v1_to_v2.rs @@ -55,10 +55,6 @@ pub fn span_v1_to_span_v2(span_v1: SpanV1) -> SpanV2 { attributes.insert("sentry.profile_id", profile_id.map_value(|v| v.to_string())); attributes.insert("sentry.platform", platform); attributes.insert("sentry.was_transaction", was_transaction); - attributes.insert( - "sentry._internal.performance_issues_spans", - performance_issues_spans, - ); // Use same precedence as `backfill_data` for data bags: if let Some(measurements) = measurements.into_value() { @@ -130,6 +126,7 @@ pub fn span_v1_to_span_v2(span_v1: SpanV1) -> SpanV2 { end_timestamp: timestamp, links: links.map_value(span_v1_links_to_span_v2_links), attributes: annotated_attributes, + performance_issues_spans, other: Default::default(), // cannot carry over because of schema mismatch } } From 2c662d0f14f09928f92f9f204e4a6de8be4d3e81 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Tue, 21 Apr 2026 17:01:54 +0200 Subject: [PATCH 2/5] test --- relay-spans/src/v1_to_v2.rs | 5 +---- tests/integration/test_spans.py | 7 +------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/relay-spans/src/v1_to_v2.rs b/relay-spans/src/v1_to_v2.rs index f6b7911e4e6..2d37935ec4b 100644 --- a/relay-spans/src/v1_to_v2.rs +++ b/relay-spans/src/v1_to_v2.rs @@ -368,10 +368,6 @@ mod tests { "type": "string", "value": "{\"numbers\":[1,2,3]}" }, - "sentry._internal.performance_issues_spans": { - "type": "boolean", - "value": true - }, "sentry.client_sample_rate": { "type": "double", "value": 0.11 @@ -429,6 +425,7 @@ mod tests { "value": true } }, + "_performance_issues_spans": true, "_meta": { "attributes": { "my.array": { diff --git a/tests/integration/test_spans.py b/tests/integration/test_spans.py index 9048e5c0906..0eabc9ac8cf 100644 --- a/tests/integration/test_spans.py +++ b/tests/integration/test_spans.py @@ -196,12 +196,7 @@ def test_span_extraction( del transaction_span["received"] if performance_issues_spans: - assert ( - transaction_span["attributes"].pop( - "sentry._internal.performance_issues_spans" - )["value"] - is True - ) + assert transaction_span.pop("_performance_issues_spans") is True expected_transaction_span = { "attributes": { From 0903f1d2eff5c47b27bd0517a780d93a09a90ba8 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Tue, 21 Apr 2026 17:29:10 +0200 Subject: [PATCH 3/5] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5abf3d9ec23..2909a8f5a85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Ports legacy standalone span processing to the processing framework. ([#5852](https://github.com/getsentry/relay/pull/5852)) - Retry failing objectstore requests. ([#5836](https://github.com/getsentry/relay/pull/5836)) - Add mobile normalizations to SpanV2 processing pipeline (mobile tag, main thread, outlier filtering, app start backfill from V1 transactions, device class). ([#5824](https://github.com/getsentry/relay/pull/5824)) +- Make _performance_issues_spans a top-level field. ([#5870](https://github.com/getsentry/relay/pull/5870)) **Bug Fixes**: From 9e40ff683c5213e05fa9ca40bc24469d7f802523 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Tue, 21 Apr 2026 17:39:55 +0200 Subject: [PATCH 4/5] Update CHANGELOG with recent changes and fixes Consolidate performance issues spans and remove deprecated config. --- CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0350840ad75..c235d8887c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,11 +18,8 @@ - Ports legacy standalone span processing to the processing framework. ([#5852](https://github.com/getsentry/relay/pull/5852)) - Retry failing objectstore requests. ([#5836](https://github.com/getsentry/relay/pull/5836)) - Add mobile normalizations to SpanV2 processing pipeline (mobile tag, main thread, outlier filtering, app start backfill from V1 transactions, device class). ([#5824](https://github.com/getsentry/relay/pull/5824)) -<<<<<<< fix/performance-issues-span -- Make _performance_issues_spans a top-level field. ([#5870](https://github.com/getsentry/relay/pull/5870)) -======= - Remove the deprecated `aiModelCosts` global config, superseded by `aiModelMetadata`. ([#5862](https://github.com/getsentry/relay/pull/5862)) ->>>>>>> master +- Make `_performance_issues_spans` a top-level field. ([#5870](https://github.com/getsentry/relay/pull/5870)) **Bug Fixes**: From c256f63bfb906b6ae62be45ab5f146dff81e47bb Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Tue, 28 Apr 2026 10:36:54 +0200 Subject: [PATCH 5/5] ref(spans): Move _performance_issues_spans from SpanV2 to SpanMeta Lift the temporary `_performance_issues_spans` flag out of the SpanV2 protocol body and onto the Kafka envelope (`SpanMeta`). The on-the-wire JSON key is unchanged because `SpanMeta` and the span body are flattened into the same object. The legacy V1 path reads the value off `SpanV1` before conversion and forwards it on `StoreSpanV2`; the native V2 path hardcodes false (it never sets this flag). Co-Authored-By: Claude Opus 4.7 (1M context) --- relay-event-schema/src/protocol/span_v2.rs | 13 ------------- .../src/processing/legacy_spans/store.rs | 6 ++++++ relay-server/src/processing/spans/store.rs | 1 + relay-server/src/services/store.rs | 16 ++++++++++++++++ relay-spans/src/v1_to_v2.rs | 4 +--- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/relay-event-schema/src/protocol/span_v2.rs b/relay-event-schema/src/protocol/span_v2.rs index 92be9a8180e..bb77e049a14 100644 --- a/relay-event-schema/src/protocol/span_v2.rs +++ b/relay-event-schema/src/protocol/span_v2.rs @@ -50,19 +50,6 @@ pub struct SpanV2 { #[metastructure(pii = "true", trim = true)] pub attributes: Annotated, - /// Temporary flag that controls where performance issues are detected. - /// - /// When the flag is set to true, performance issues will be detected on this span provided it - /// is a root (segment) instead of the transaction event. - /// - /// Only set on root spans extracted from transactions. - #[metastructure( - field = "_performance_issues_spans", - skip_serialization = "empty", - trim = false - )] - pub performance_issues_spans: Annotated, - /// Additional arbitrary fields for forwards compatibility. #[metastructure(additional_properties, pii = "maybe")] pub other: Object, diff --git a/relay-server/src/processing/legacy_spans/store.rs b/relay-server/src/processing/legacy_spans/store.rs index 791cde6ba6a..d9b48383b97 100644 --- a/relay-server/src/processing/legacy_spans/store.rs +++ b/relay-server/src/processing/legacy_spans/store.rs @@ -23,6 +23,11 @@ macro_rules! required { /// Converts a [`Span`] into a [`StoreSpanV2`] to be sent to Kafka. pub fn convert(span: Annotated, retentions: Retention) -> Result> { + let performance_issues_spans = span + .value() + .and_then(|s| s.performance_issues_spans.value().copied()) + .unwrap_or(false); + let span = span.map_value(relay_spans::span_v1_to_span_v2); let span = required!(span); @@ -30,6 +35,7 @@ pub fn convert(span: Annotated, retentions: Retention) -> Result Result> routing_key, retention_days: ctx.retention.standard, downsampled_retention_days: ctx.retention.downsampled, + performance_issues_spans: false, item: span, })) } diff --git a/relay-server/src/services/store.rs b/relay-server/src/services/store.rs index 4fba25dbdd3..e7adea9423e 100644 --- a/relay-server/src/services/store.rs +++ b/relay-server/src/services/store.rs @@ -137,6 +137,10 @@ pub struct StoreSpanV2 { pub retention_days: u16, /// Downsampled retention of the span. pub downsampled_retention_days: u16, + /// Temporary flag controlling where performance issues are detected. + /// + /// Travels on the Kafka envelope (`SpanMeta`) rather than the SpanV2 body. + pub performance_issues_spans: bool, /// The final Sentry compatible span item. pub item: SpanV2, } @@ -788,6 +792,7 @@ impl StoreService { downsampled_retention_days: message.downsampled_retention_days, received: datetime_to_timestamp(received_at), accepted_outcome_emitted: relay_emits_accepted_outcome, + performance_issues_spans: message.performance_issues_spans, }; message.try_accept(|span| { @@ -1677,6 +1682,17 @@ struct SpanMeta { downsampled_retention_days: u16, /// Indicates whether Relay already emitted an accepted outcome or if EAP still needs to emit it. accepted_outcome_emitted: bool, + /// Temporary flag that controls where performance issues are detected. + /// + /// When the flag is set to true, performance issues will be detected on this span provided it + /// is a root (segment) instead of the transaction event. + /// + /// Only set on root spans extracted from transactions. + #[serde( + rename = "_performance_issues_spans", + skip_serializing_if = "std::ops::Not::not" + )] + performance_issues_spans: bool, } #[derive(Clone, Debug, Serialize)] diff --git a/relay-spans/src/v1_to_v2.rs b/relay-spans/src/v1_to_v2.rs index 2d37935ec4b..e1c41c75c52 100644 --- a/relay-spans/src/v1_to_v2.rs +++ b/relay-spans/src/v1_to_v2.rs @@ -38,7 +38,7 @@ pub fn span_v1_to_span_v2(span_v1: SpanV1) -> SpanV2 { platform, was_transaction, kind, - performance_issues_spans, + performance_issues_spans: _, // moved to SpanMeta on the Kafka envelope other: _, } = span_v1; @@ -126,7 +126,6 @@ pub fn span_v1_to_span_v2(span_v1: SpanV1) -> SpanV2 { end_timestamp: timestamp, links: links.map_value(span_v1_links_to_span_v2_links), attributes: annotated_attributes, - performance_issues_spans, other: Default::default(), // cannot carry over because of schema mismatch } } @@ -425,7 +424,6 @@ mod tests { "value": true } }, - "_performance_issues_spans": true, "_meta": { "attributes": { "my.array": {