Skip to content

UnsampledTransactions to reduce memory pressure #4212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 29, 2025

Conversation

jamescrosswell
Copy link
Collaborator

@jamescrosswell jamescrosswell commented May 22, 2025

Added a light weight UnsampledTransaction class to be used when transactions are not sampled. The aim is to reduce memory pressure when sampling less than 100% of transactions.

Resolves #3636:

Replaces #3972:

Added a light weight `UnsampledTransaction` class to be used instead of a full blown `TransactionTracer` when transactions are not sampled. The aim is to reduce memory pressure when sampling less than 100% of transactions.

Replaces #3972:
- #3972
Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

I couldn't spot a test that validates we're still sending the trace-id downstream from http requests for example. That's one concern I have, if we have an unsampled transaction, the trace id should still be used to link all data leaving the SDK (errors, user feedback, replay) as well as all outgoing HTTP request should have that trace id.

Comment on lines +13 to +14
// report to sentry is the span count (in the client report), SDK users may refer to things like
// `ITransaction.Spans.Count`, so we create an actual collection
Copy link
Member

Choose a reason for hiding this comment

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

what's the use case for users to get count of spans from transactions?

the goal is to deprecate transactions in the long term. Spans are expected to stream out of the SDK
Anything done absed on span count is going to break so I wouldn't optimzie for that

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's for the client report. At the moment the sampling is done at the transaction level... so if a transaction gets sampled out we're sampling out the transaction + it's tree of child spans. We report the number of spans we sampled out in the client report.

/// avoid lots of unecessary processing. The only thing we need to track is the number of spans that would have been
/// created (the client reports detailing discarded events includes this detail).
/// </summary>
internal sealed class UnsampledTransaction : NoOpTransaction
Copy link
Member

Choose a reason for hiding this comment

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

I was hoping we'd get away without having to create an instance for each transaction but I also dunno how we'd deal with trace propagation in that case. And since most of the allocation are on the spans anyway this prob going to shave off enough GC pressure

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, especially since we have to propagate things like the SampleRand, we need separate instances of UnsampledTransaction.

As well as not having to allocate anything for child spans, the UnsampledTransaction class itself is quite a bit lighter than TransactionTracer (no Contexts, Breadcrumbs, Extras etc.). It should make a big difference in things like high volume ASP.NET Core apps.

@jamescrosswell
Copy link
Collaborator Author

jamescrosswell commented May 29, 2025

I couldn't spot a test that validates we're still sending the trace-id downstream from http requests for example. That's one concern I have, if we have an unsampled transaction, the trace id should still be used to link all data leaving the SDK

Good call. I added some tests to the Hub since the Baggage and Trace headers that get propagated get read off the currently Active span in the Hub:

if (_options?.TracePropagationTargets.MatchesSubstringOrRegex(url) is true or null)
{
AddSentryTraceHeader(request);
AddBaggageHeader(request);
}
}
private void AddSentryTraceHeader(HttpRequestMessage request)
{
// Set trace header if it hasn't already been set
if (!request.Headers.Contains(SentryTraceHeader.HttpHeaderName) && _hub.GetTraceHeader() is { } traceHeader)
{
request.Headers.Add(SentryTraceHeader.HttpHeaderName, traceHeader.ToString());
}
}
private void AddBaggageHeader(HttpRequestMessage request)
{
var baggage = _hub.GetBaggage();

@jamescrosswell jamescrosswell merged commit aebd6a2 into main May 29, 2025
35 checks passed
@jamescrosswell jamescrosswell deleted the unsampled-transactions2 branch May 29, 2025 08:17
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.

Sampling based Transaction.NoOp to easy memory pressure
3 participants