Skip to content

Commit ef2f35d

Browse files
authored
ref(opentelemetry): Avoid sampling work for non-root spans (#15820)
Related to #15725 (comment) When a span is not a root span, we do not need to do sampling work for it. By reordering stuff in the `shouldSample` function, we should be able to save a bunch of operations for the vast majority of (non-root) spans. I _think_ this should be just fine!
1 parent 2284e81 commit ef2f35d

File tree

1 file changed

+62
-67
lines changed

1 file changed

+62
-67
lines changed

packages/opentelemetry/src/sampler.ts

+62-67
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ export class SentrySampler implements Sampler {
6767
}
6868

6969
const parentSampled = parentSpan ? getParentSampled(parentSpan, traceId, spanName) : undefined;
70+
const isRootSpan = !parentSpan || parentContext?.isRemote;
71+
72+
// We only sample based on parameters (like tracesSampleRate or tracesSampler) for root spans (which is done in sampleSpan).
73+
// Non-root-spans simply inherit the sampling decision from their parent.
74+
if (!isRootSpan) {
75+
return wrapSamplingDecision({
76+
decision: parentSampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,
77+
context,
78+
spanAttributes,
79+
});
80+
}
7081

7182
// We want to pass the inferred name & attributes to the sampler method
7283
const {
@@ -99,76 +110,60 @@ export class SentrySampler implements Sampler {
99110
return wrapSamplingDecision({ decision: undefined, context, spanAttributes });
100111
}
101112

102-
const isRootSpan = !parentSpan || parentContext?.isRemote;
113+
const { isolationScope } = getScopesFromContext(context) ?? {};
103114

104-
// We only sample based on parameters (like tracesSampleRate or tracesSampler) for root spans (which is done in sampleSpan).
105-
// Non-root-spans simply inherit the sampling decision from their parent.
106-
if (isRootSpan) {
107-
const { isolationScope } = getScopesFromContext(context) ?? {};
108-
109-
const dscString = parentContext?.traceState ? parentContext.traceState.get(SENTRY_TRACE_STATE_DSC) : undefined;
110-
const dsc = dscString ? baggageHeaderToDynamicSamplingContext(dscString) : undefined;
111-
112-
const sampleRand = parseSampleRate(dsc?.sample_rand) ?? Math.random();
113-
114-
const [sampled, sampleRate, localSampleRateWasApplied] = sampleSpan(
115-
options,
116-
{
117-
name: inferredSpanName,
118-
attributes: mergedAttributes,
119-
normalizedRequest: isolationScope?.getScopeData().sdkProcessingMetadata.normalizedRequest,
120-
parentSampled,
121-
parentSampleRate: parseSampleRate(dsc?.sample_rate),
122-
},
115+
const dscString = parentContext?.traceState ? parentContext.traceState.get(SENTRY_TRACE_STATE_DSC) : undefined;
116+
const dsc = dscString ? baggageHeaderToDynamicSamplingContext(dscString) : undefined;
117+
118+
const sampleRand = parseSampleRate(dsc?.sample_rand) ?? Math.random();
119+
120+
const [sampled, sampleRate, localSampleRateWasApplied] = sampleSpan(
121+
options,
122+
{
123+
name: inferredSpanName,
124+
attributes: mergedAttributes,
125+
normalizedRequest: isolationScope?.getScopeData().sdkProcessingMetadata.normalizedRequest,
126+
parentSampled,
127+
parentSampleRate: parseSampleRate(dsc?.sample_rate),
128+
},
129+
sampleRand,
130+
);
131+
132+
const method = `${maybeSpanHttpMethod}`.toUpperCase();
133+
if (method === 'OPTIONS' || method === 'HEAD') {
134+
DEBUG_BUILD && logger.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`);
135+
136+
return wrapSamplingDecision({
137+
decision: SamplingDecision.NOT_RECORD,
138+
context,
139+
spanAttributes,
123140
sampleRand,
124-
);
125-
126-
const method = `${maybeSpanHttpMethod}`.toUpperCase();
127-
if (method === 'OPTIONS' || method === 'HEAD') {
128-
DEBUG_BUILD && logger.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`);
129-
130-
return {
131-
...wrapSamplingDecision({
132-
decision: SamplingDecision.NOT_RECORD,
133-
context,
134-
spanAttributes,
135-
sampleRand,
136-
downstreamTraceSampleRate: 0, // we don't want to sample anything in the downstream trace either
137-
}),
138-
};
139-
}
140-
141-
if (
142-
!sampled &&
143-
// We check for `parentSampled === undefined` because we only want to record client reports for spans that are trace roots (ie. when there was incoming trace)
144-
parentSampled === undefined
145-
) {
146-
DEBUG_BUILD && logger.log('[Tracing] Discarding root span because its trace was not chosen to be sampled.');
147-
this._client.recordDroppedEvent('sample_rate', 'transaction');
148-
}
149-
150-
return {
151-
...wrapSamplingDecision({
152-
decision: sampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,
153-
context,
154-
spanAttributes,
155-
sampleRand,
156-
downstreamTraceSampleRate: localSampleRateWasApplied ? sampleRate : undefined,
157-
}),
158-
attributes: {
159-
// We set the sample rate on the span when a local sample rate was applied to better understand how traces were sampled in Sentry
160-
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: localSampleRateWasApplied ? sampleRate : undefined,
161-
},
162-
};
163-
} else {
164-
return {
165-
...wrapSamplingDecision({
166-
decision: parentSampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,
167-
context,
168-
spanAttributes,
169-
}),
170-
};
141+
downstreamTraceSampleRate: 0, // we don't want to sample anything in the downstream trace either
142+
});
143+
}
144+
145+
if (
146+
!sampled &&
147+
// We check for `parentSampled === undefined` because we only want to record client reports for spans that are trace roots (ie. when there was incoming trace)
148+
parentSampled === undefined
149+
) {
150+
DEBUG_BUILD && logger.log('[Tracing] Discarding root span because its trace was not chosen to be sampled.');
151+
this._client.recordDroppedEvent('sample_rate', 'transaction');
171152
}
153+
154+
return {
155+
...wrapSamplingDecision({
156+
decision: sampled ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD,
157+
context,
158+
spanAttributes,
159+
sampleRand,
160+
downstreamTraceSampleRate: localSampleRateWasApplied ? sampleRate : undefined,
161+
}),
162+
attributes: {
163+
// We set the sample rate on the span when a local sample rate was applied to better understand how traces were sampled in Sentry
164+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: localSampleRateWasApplied ? sampleRate : undefined,
165+
},
166+
};
172167
}
173168

174169
/** Returns the sampler name or short description with the configuration. */

0 commit comments

Comments
 (0)