Skip to content

Commit 01104fb

Browse files
authored
fix(browser): Correctly parse sampleRate when consistentTraceSampling is enabled (#21281)
This PR fixes 2 bugs in our trace linking logic that would emerge when `consistentTraceSampling` was enabled: 1. We'd incorrectly parse an `undefined` sample rate as `0` on the last DSC instead of falling back to the sampling rate on the span 2. The order of checking for the sample rate on the span vs. DSC was incorrect. This just didn't surface because of bug 1 closes #21264
1 parent 0613ef7 commit 01104fb

2 files changed

Lines changed: 129 additions & 4 deletions

File tree

packages/browser/src/tracing/linkedTraces.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,10 @@ export function addPreviousTraceSpanLink(
141141

142142
function getSampleRate(): number {
143143
try {
144-
return (
145-
Number(oldPropagationContext.dsc?.sample_rate) ?? Number(spanJson.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE])
144+
const oldSampleRate = Number(
145+
spanJson.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE] ?? oldPropagationContext.dsc?.sample_rate,
146146
);
147+
return Number.isNaN(oldSampleRate) ? 0 : oldSampleRate;
147148
} catch {
148149
return 0;
149150
}

packages/browser/test/tracing/linkedTraces.test.ts

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
import type { Span } from '@sentry/core/browser';
2-
import { addChildSpanToSpan, debug, SentrySpan, spanToJSON, timestampInSeconds } from '@sentry/core/browser';
1+
import type { PropagationContext, Span } from '@sentry/core/browser';
2+
import {
3+
addChildSpanToSpan,
4+
debug,
5+
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
6+
SentrySpan,
7+
spanToJSON,
8+
timestampInSeconds,
9+
} from '@sentry/core/browser';
310
import { beforeEach, describe, expect, it, vi } from 'vitest';
411
import { BrowserClient } from '../../src';
512
import type { PreviousTraceInfo } from '../../src/tracing/linkedTraces';
@@ -201,6 +208,123 @@ describe('addPreviousTraceSpanLink', () => {
201208
});
202209
});
203210

211+
it.each([NaN, undefined])(
212+
'falls back to sampleRate 0 if the sample rate in the previous trace info is %s',
213+
sampleRate => {
214+
const currentSpanStart = timestampInSeconds();
215+
216+
const previousTraceInfo: PreviousTraceInfo = {
217+
spanContext: { traceId: '123', spanId: '456', traceFlags: 1 },
218+
startTimestamp: currentSpanStart - PREVIOUS_TRACE_MAX_DURATION + 1,
219+
sampleRand: 0.0126,
220+
sampleRate: sampleRate as number,
221+
};
222+
223+
const currentSpan = new SentrySpan({
224+
name: 'test',
225+
startTimestamp: currentSpanStart,
226+
parentSpanId: '789',
227+
spanId: 'abc',
228+
traceId: 'def',
229+
sampled: true,
230+
});
231+
232+
const oldPropagationContext: PropagationContext = {
233+
sampleRand: 0.0126,
234+
traceId: '123',
235+
sampled: true,
236+
dsc: { sample_rand: '0.0126', sample_rate: String(sampleRate) },
237+
};
238+
239+
const updatedPreviousTraceInfo = addPreviousTraceSpanLink(previousTraceInfo, currentSpan, oldPropagationContext);
240+
241+
expect(updatedPreviousTraceInfo).toEqual({
242+
spanContext: currentSpan.spanContext(),
243+
startTimestamp: currentSpanStart,
244+
sampleRand: 0.0126,
245+
sampleRate: 0,
246+
});
247+
},
248+
);
249+
250+
it.each([NaN, undefined])('falls back to sampleRate 0 if the sample rate on the spa or DSC is %s', sampleRate => {
251+
const currentSpanStart = timestampInSeconds();
252+
253+
const previousTraceInfo: PreviousTraceInfo = {
254+
spanContext: { traceId: '123', spanId: '456', traceFlags: 1 },
255+
startTimestamp: currentSpanStart - PREVIOUS_TRACE_MAX_DURATION + 1,
256+
sampleRand: 0.0126,
257+
sampleRate: 0,
258+
};
259+
260+
const currentSpan = new SentrySpan({
261+
name: 'test',
262+
startTimestamp: currentSpanStart,
263+
parentSpanId: '789',
264+
spanId: 'abc',
265+
traceId: 'def',
266+
sampled: true,
267+
attributes: {
268+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: sampleRate,
269+
},
270+
});
271+
272+
const oldPropagationContext: PropagationContext = {
273+
sampleRand: 0.0126,
274+
traceId: '123',
275+
sampled: true,
276+
dsc: { sample_rand: '0.0126', sample_rate: String(sampleRate) },
277+
};
278+
279+
const updatedPreviousTraceInfo = addPreviousTraceSpanLink(previousTraceInfo, currentSpan, oldPropagationContext);
280+
281+
expect(updatedPreviousTraceInfo).toEqual({
282+
spanContext: currentSpan.spanContext(),
283+
startTimestamp: currentSpanStart,
284+
sampleRand: 0.0126,
285+
sampleRate: 0,
286+
});
287+
});
288+
289+
it('prefers sample rate from span over sample rate from DSC', () => {
290+
const currentSpanStart = timestampInSeconds();
291+
292+
const previousTraceInfo: PreviousTraceInfo = {
293+
spanContext: { traceId: '123', spanId: '456', traceFlags: 1 },
294+
startTimestamp: currentSpanStart - PREVIOUS_TRACE_MAX_DURATION + 1,
295+
sampleRand: 0.0126,
296+
sampleRate: 0.4,
297+
};
298+
299+
const currentSpan = new SentrySpan({
300+
name: 'test',
301+
startTimestamp: currentSpanStart,
302+
parentSpanId: '789',
303+
spanId: 'abc',
304+
traceId: 'def',
305+
sampled: true,
306+
attributes: {
307+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 0.5,
308+
},
309+
});
310+
311+
const oldPropagationContext: PropagationContext = {
312+
sampleRand: 0.0126,
313+
traceId: '123',
314+
sampled: true,
315+
dsc: { sample_rand: '0.0126', sample_rate: '0.6' },
316+
};
317+
318+
const updatedPreviousTraceInfo = addPreviousTraceSpanLink(previousTraceInfo, currentSpan, oldPropagationContext);
319+
320+
expect(updatedPreviousTraceInfo).toEqual({
321+
spanContext: currentSpan.spanContext(),
322+
startTimestamp: currentSpanStart,
323+
sampleRand: 0.0126,
324+
sampleRate: 0.5,
325+
});
326+
});
327+
204328
it('logs a debug message when adding a previous trace link (with stringified context)', () => {
205329
const debugLogSpy = vi.spyOn(debug, 'log');
206330

0 commit comments

Comments
 (0)