Conversation
Contributor
Codecov Results 📊Generated by Codecov Action |
Contributor
node-overhead report 🧳Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.
|
Lms24
added a commit
that referenced
this pull request
Feb 9, 2026
This PR adds a simple span buffer implementation to be used for buffering streamed spans. Behaviour: - buckets incoming spans by `traceId`, as we must not mix up spans of different traces in one envelope - flushes the entire buffer every 5s by default - flushes the specific trace bucket if the max span limit (1000) is reached. Relay accepts at max. 1000 spans per envelope - computes the DSC when flushing the first span of a trace. This is the latest time we can do it as once we flushed we have to freeze the DSC for Dynamic Sampling consistency - debounces the flush interval whenever we flush - flushes the entire buffer if `Sentry.flush()` is called - shuts down the interval-based flushing when `Sentry.close()` is called - [implicit] Client report generation for dropped envelopes is handled in the transport Methods: - `add` accepts a new span to be enqueued into the buffer - `drain` flushes the entire buffer - `flush(traceId)` flushes a specific traceId bucket. This can be used by e.g. the browser span streaming implementation to flush out the trace of a segment span directly once it ends. Options: - `maxSpanLimit` - allows to configure a 0 < maxSpanLimit < 1000 custom span limit. Useful for testing but we could also expose this to users if we see a need - `flushInterval`- allows to configure a >0 flush interval Limitations/edge cases: - No maximum limit of concurrently buffered traces. I'd tend to accept this for now and see where this leads us in terms of memory pressure but at the end of the day, the interval based flushing, in combination with our promise buffer _should_ avoid an ever-growing map of trace buckets. Happy to change this if reviewers have strong opinions or I'm missing something important! - There's no priority based scheduling relative to other telemetry items. Just like with our other log and metric buffers. - since `Map` is [insertion order preserving](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description), we apply a FIFO strategy when`drain`ing the trace buckets. This is in line with our [develop spec](https://develop.sentry.dev/sdk/telemetry/telemetry-processor/backend-telemetry-processor/#:~:text=The%20span%20buffer,in%20the%20buffer.) for the telemetry processor but might lead to cases where new traces are dropped by the promise buffer if a lof of concurrently running traces are flushed. I think that's a fine trade off. ref #19119
Contributor
size-limit report 📦
|
28441df to
bad8399
Compare
Lms24
added a commit
that referenced
this pull request
Feb 13, 2026
This PR adds a simple span buffer implementation to be used for buffering streamed spans. Behaviour: - buckets incoming spans by `traceId`, as we must not mix up spans of different traces in one envelope - flushes the entire buffer every 5s by default - flushes the specific trace bucket if the max span limit (1000) is reached. Relay accepts at max. 1000 spans per envelope - computes the DSC when flushing the first span of a trace. This is the latest time we can do it as once we flushed we have to freeze the DSC for Dynamic Sampling consistency - debounces the flush interval whenever we flush - flushes the entire buffer if `Sentry.flush()` is called - shuts down the interval-based flushing when `Sentry.close()` is called - [implicit] Client report generation for dropped envelopes is handled in the transport Methods: - `add` accepts a new span to be enqueued into the buffer - `drain` flushes the entire buffer - `flush(traceId)` flushes a specific traceId bucket. This can be used by e.g. the browser span streaming implementation to flush out the trace of a segment span directly once it ends. Options: - `maxSpanLimit` - allows to configure a 0 < maxSpanLimit < 1000 custom span limit. Useful for testing but we could also expose this to users if we see a need - `flushInterval`- allows to configure a >0 flush interval Limitations/edge cases: - No maximum limit of concurrently buffered traces. I'd tend to accept this for now and see where this leads us in terms of memory pressure but at the end of the day, the interval based flushing, in combination with our promise buffer _should_ avoid an ever-growing map of trace buckets. Happy to change this if reviewers have strong opinions or I'm missing something important! - There's no priority based scheduling relative to other telemetry items. Just like with our other log and metric buffers. - since `Map` is [insertion order preserving](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description), we apply a FIFO strategy when`drain`ing the trace buckets. This is in line with our [develop spec](https://develop.sentry.dev/sdk/telemetry/telemetry-processor/backend-telemetry-processor/#:~:text=The%20span%20buffer,in%20the%20buffer.) for the telemetry processor but might lead to cases where new traces are dropped by the promise buffer if a lof of concurrently running traces are flushed. I think that's a fine trade off. ref #19119
bad8399 to
5a165d4
Compare
Lms24
added a commit
that referenced
this pull request
Feb 16, 2026
This PR adds a simple span buffer implementation to be used for buffering streamed spans. Behaviour: - buckets incoming spans by `traceId`, as we must not mix up spans of different traces in one envelope - flushes the entire buffer every 5s by default - flushes the specific trace bucket if the max span limit (1000) is reached. Relay accepts at max. 1000 spans per envelope - computes the DSC when flushing the first span of a trace. This is the latest time we can do it as once we flushed we have to freeze the DSC for Dynamic Sampling consistency - debounces the flush interval whenever we flush - flushes the entire buffer if `Sentry.flush()` is called - shuts down the interval-based flushing when `Sentry.close()` is called - [implicit] Client report generation for dropped envelopes is handled in the transport Methods: - `add` accepts a new span to be enqueued into the buffer - `drain` flushes the entire buffer - `flush(traceId)` flushes a specific traceId bucket. This can be used by e.g. the browser span streaming implementation to flush out the trace of a segment span directly once it ends. Options: - `maxSpanLimit` - allows to configure a 0 < maxSpanLimit < 1000 custom span limit. Useful for testing but we could also expose this to users if we see a need - `flushInterval`- allows to configure a >0 flush interval Limitations/edge cases: - No maximum limit of concurrently buffered traces. I'd tend to accept this for now and see where this leads us in terms of memory pressure but at the end of the day, the interval based flushing, in combination with our promise buffer _should_ avoid an ever-growing map of trace buckets. Happy to change this if reviewers have strong opinions or I'm missing something important! - There's no priority based scheduling relative to other telemetry items. Just like with our other log and metric buffers. - since `Map` is [insertion order preserving](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description), we apply a FIFO strategy when`drain`ing the trace buckets. This is in line with our [develop spec](https://develop.sentry.dev/sdk/telemetry/telemetry-processor/backend-telemetry-processor/#:~:text=The%20span%20buffer,in%20the%20buffer.) for the telemetry processor but might lead to cases where new traces are dropped by the promise buffer if a lof of concurrently running traces are flushed. I think that's a fine trade off. ref #19119
Lms24
added a commit
that referenced
this pull request
Mar 2, 2026
This PR adds a simple span buffer implementation to be used for buffering streamed spans. Behaviour: - buckets incoming spans by `traceId`, as we must not mix up spans of different traces in one envelope - flushes the entire buffer every 5s by default - flushes the specific trace bucket if the max span limit (1000) is reached. Relay accepts at max. 1000 spans per envelope - computes the DSC when flushing the first span of a trace. This is the latest time we can do it as once we flushed we have to freeze the DSC for Dynamic Sampling consistency - debounces the flush interval whenever we flush - flushes the entire buffer if `Sentry.flush()` is called - shuts down the interval-based flushing when `Sentry.close()` is called - [implicit] Client report generation for dropped envelopes is handled in the transport Methods: - `add` accepts a new span to be enqueued into the buffer - `drain` flushes the entire buffer - `flush(traceId)` flushes a specific traceId bucket. This can be used by e.g. the browser span streaming implementation to flush out the trace of a segment span directly once it ends. Options: - `maxSpanLimit` - allows to configure a 0 < maxSpanLimit < 1000 custom span limit. Useful for testing but we could also expose this to users if we see a need - `flushInterval`- allows to configure a >0 flush interval Limitations/edge cases: - No maximum limit of concurrently buffered traces. I'd tend to accept this for now and see where this leads us in terms of memory pressure but at the end of the day, the interval based flushing, in combination with our promise buffer _should_ avoid an ever-growing map of trace buckets. Happy to change this if reviewers have strong opinions or I'm missing something important! - There's no priority based scheduling relative to other telemetry items. Just like with our other log and metric buffers. - since `Map` is [insertion order preserving](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description), we apply a FIFO strategy when`drain`ing the trace buckets. This is in line with our [develop spec](https://develop.sentry.dev/sdk/telemetry/telemetry-processor/backend-telemetry-processor/#:~:text=The%20span%20buffer,in%20the%20buffer.) for the telemetry processor but might lead to cases where new traces are dropped by the promise buffer if a lof of concurrently running traces are flushed. I think that's a fine trade off. ref #19119
24015b1 to
e5c1208
Compare
Lms24
added a commit
that referenced
this pull request
Mar 4, 2026
This PR adds a simple span buffer implementation to be used for buffering streamed spans. Behaviour: - buckets incoming spans by `traceId`, as we must not mix up spans of different traces in one envelope - flushes the entire buffer every 5s by default - flushes the specific trace bucket if the max span limit (1000) is reached. Relay accepts at max. 1000 spans per envelope - computes the DSC when flushing the first span of a trace. This is the latest time we can do it as once we flushed we have to freeze the DSC for Dynamic Sampling consistency - debounces the flush interval whenever we flush - flushes the entire buffer if `Sentry.flush()` is called - shuts down the interval-based flushing when `Sentry.close()` is called - [implicit] Client report generation for dropped envelopes is handled in the transport Methods: - `add` accepts a new span to be enqueued into the buffer - `drain` flushes the entire buffer - `flush(traceId)` flushes a specific traceId bucket. This can be used by e.g. the browser span streaming implementation to flush out the trace of a segment span directly once it ends. Options: - `maxSpanLimit` - allows to configure a 0 < maxSpanLimit < 1000 custom span limit. Useful for testing but we could also expose this to users if we see a need - `flushInterval`- allows to configure a >0 flush interval Limitations/edge cases: - No maximum limit of concurrently buffered traces. I'd tend to accept this for now and see where this leads us in terms of memory pressure but at the end of the day, the interval based flushing, in combination with our promise buffer _should_ avoid an ever-growing map of trace buckets. Happy to change this if reviewers have strong opinions or I'm missing something important! - There's no priority based scheduling relative to other telemetry items. Just like with our other log and metric buffers. - since `Map` is [insertion order preserving](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description), we apply a FIFO strategy when`drain`ing the trace buckets. This is in line with our [develop spec](https://develop.sentry.dev/sdk/telemetry/telemetry-processor/backend-telemetry-processor/#:~:text=The%20span%20buffer,in%20the%20buffer.) for the telemetry processor but might lead to cases where new traces are dropped by the promise buffer if a lof of concurrently running traces are flushed. I think that's a fine trade off. ref #19119
This PR introduces span v2 types as defined in our [develop spec](https://develop.sentry.dev/sdk/telemetry/spans/span-protocol/): * Envelope types: * `SpanV2Envelope`, `SpanV2EnvelopeHeaders`, `SpanContainerItem`, `SpanContainerItemHeaders` * Span v2 types: * `SpanV2JSON` the equivalent to today's `SpanJSON`. Users will interact with spans in this format in `beforeSendSpan`. SDK integrations will use this format in `processSpan` (and related) hooks. * `SerializedSpan` the final, serialized format for v2 spans, sent in the envelope container item. Closes #19101 (added automatically) ref #17836
…ility utilities (#19120) This adds the foundation for user-facing span streaming configuration: - **`traceLifecycle` option**: New option in `ClientOptions` that controls whether spans are sent statically (when the entire local span tree is complete) or streamed (in batches following interval- and action-based triggers). Because the span JSON will look different for streamed spans vs. static spans (i.e. our current ones, we also need some helpers for `beforeSendSpan` where users consume and interact with `StreamedSpanJSON`: - **`withStreamedSpan()` utility**: Wrapper function that marks a `beforeSendSpan` callback as compatible with the streamed span format (`StreamedSpanJSON`) - **`isStreamedBeforeSendSpanCallback()` type guard**: Internal utility to check if a callback was wrapped with `withStreamedSpan`
Adds a utility to create a span v2 envelope from a `SerializedSpan` array + tests. Note: I think here, the "v2" naming makes more sense than the `StreamSpan` patter we use for user-facing functionality. This function should never be called by users, and the envelope is type `span` with content type `span.v2+json` ref #17836
This PR adds span JSON conversion and serialization helpers for span streaming: * `spanToStreamedSpanJSON`: Converts a `Span` instance to a JSON object used as intermediate representation as outlined in #19100 * Adds `SentrySpan::getStreamedSpanJSON` method to convert our own spans * Directly converts any OTel spans * This is analogous to how `spanToJSON` works today. * `spanJsonToSerializedSpan`: Converts a `StreamedSpanJSON` into the final `SerializedSpan` to be sent to Sentry. This PR also adds unit tests for both helpers. ref #17836 --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Jan Peer Stöcklmair <jan.peer@sentry.io>
This PR adds the `captureSpan` pipeline, which takes a `Span` instance, processes it and ultimately returns a `SerializedStreamedSpan` which can then be enqueued into the span buffer. ref #17836
This PR adds a simple span buffer implementation to be used for buffering streamed spans. Behaviour: - buckets incoming spans by `traceId`, as we must not mix up spans of different traces in one envelope - flushes the entire buffer every 5s by default - flushes the specific trace bucket if the max span limit (1000) is reached. Relay accepts at max. 1000 spans per envelope - computes the DSC when flushing the first span of a trace. This is the latest time we can do it as once we flushed we have to freeze the DSC for Dynamic Sampling consistency - debounces the flush interval whenever we flush - flushes the entire buffer if `Sentry.flush()` is called - shuts down the interval-based flushing when `Sentry.close()` is called - [implicit] Client report generation for dropped envelopes is handled in the transport Methods: - `add` accepts a new span to be enqueued into the buffer - `drain` flushes the entire buffer - `flush(traceId)` flushes a specific traceId bucket. This can be used by e.g. the browser span streaming implementation to flush out the trace of a segment span directly once it ends. Options: - `maxSpanLimit` - allows to configure a 0 < maxSpanLimit < 1000 custom span limit. Useful for testing but we could also expose this to users if we see a need - `flushInterval`- allows to configure a >0 flush interval Limitations/edge cases: - No maximum limit of concurrently buffered traces. I'd tend to accept this for now and see where this leads us in terms of memory pressure but at the end of the day, the interval based flushing, in combination with our promise buffer _should_ avoid an ever-growing map of trace buckets. Happy to change this if reviewers have strong opinions or I'm missing something important! - There's no priority based scheduling relative to other telemetry items. Just like with our other log and metric buffers. - since `Map` is [insertion order preserving](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#description), we apply a FIFO strategy when`drain`ing the trace buckets. This is in line with our [develop spec](https://develop.sentry.dev/sdk/telemetry/telemetry-processor/backend-telemetry-processor/#:~:text=The%20span%20buffer,in%20the%20buffer.) for the telemetry processor but might lead to cases where new traces are dropped by the promise buffer if a lof of concurrently running traces are flushed. I think that's a fine trade off. ref #19119
This PR adds the final big building block for span streaming functionality in the browser SDK: `spanStreamingIntegation`. This integration: - enables `traceLifecycle: 'stream'` if not already set by users. This allows us to avoid the double-opt-in problem we usually have in browser SDKs because we want to keep integration tree-shakeable but also support the runtime-agnostic `traceLifecycle` option. - to do this properly, I decided to introduce a new integration hook: `beforeSetup`. This is allows us to safely modify client options before other integrations read it. We'll need this because `browserTracingIntegration` needs to check for span streaming later on. Let me know what you think! - validates that `beforeSendSpan` is compatible with span streaming. If not, it falls back to static tracing (transactions). - listens to a new `afterSpanEnd` hook. Once called, it will capture the span and hand it off to the span buffer. - listens to a new `afterSegmentSpanEnd` hook. Once called it will flush the trace from the buffer to ensure we flush out the trace as soon as possible. In browser, it's more likely that users refresh or close the tab/window before our buffer's internal flush interval triggers. We don't _have_ to do this but I figured it would be a good trigger point. While "final building block" sounds nice, there's still a lot of stuff to take care of in the browser. But with this in place we can also start integration-testing the browser SDKs. ref #17836 --------- Co-authored-by: Jan Peer Stöcklmair <jan.peer@sentry.io>
Adds weight-based flushing and span size estimation to the span buffer. Behaviour: - tracks weight independently per trace - weight estimation follows the same strategy we use for logs and metrics. I optimized the calculation, adding fixed sizes for as many fields as possible. Only span name, attributes and links are computed dynamically, with the same assumptions and considerations as in logs and metrics. - My tests show that the size estimation roughly compares to factor 0.8 to 1.2 to the real sizes, depending on data on spans (no, few, many, primitive, array attributes and links, etc.) - For now, the limit is set to 5MB which is half of the 10MB Relay accepts for span envelopes.
e5c1208 to
12c9b29
Compare
This PR adds browser integration to test testing span streaming: - Added test helpers: - `waitForStreamedSpan`: Returns a promise of a single matching span - `waitForStreamedSpans`: Returns a promise of all spans in an array whenever the callback returns true - `waitForStreamedSpanEnvelope`: Returns an entire streamed span (v2) envelope (including headers) - `observeStreamedSpan`: Can be used to observe sent span envelopes without blocking the test if no envelopes are sent (good for testing that spans are _not_ sent) - `getSpanOp`: Small helper to easily get the op of a span which we almost always need for the `waitFor*` function callbacks Added 50+ tests, mostly converted from transaction integration tests around spans from `browserTracingIntegration`: - tests asserting the entire span v2 envelope payloads of manually started, pageload and navigation span trees - tests for trace linking and trace lifetime - tests for spans coming from browserTracingIntegration (fetch, xhr, long animation frame, long tasks) Also, this PR fixes two bugs discovered through tests: - negatively sampled spans were still sent (because non-recording spans go through the same span life cycle) - cancelled spans received status `error` instead of `ok`. We want them to have status `ok` but an attribute detailing the cancellation reason. Lastly, I discovered a problem with timing data on fetch and XHR spans. Will try to fix as a follow-up. Tracked in #19613 ref #17836
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR collects all Span Streaming (Span First) PRs. Draft for now but this will eventually be merged into
develop.So far, merged:
traceLifecycleoption andbeforeSendSpancompatibility utilities #19120StreamedSpanEnvelopecreation function #19153captureSpanpipeline and helpers #19197SpanBufferimplementation #19204http.clientspans #19643closes #17836