Skip to content

Commit 65162a2

Browse files
authored
fix(core): Avoid prolonging idle span when starting standalone span (#16928)
When we we start and send a web vital standalone span while an idlespan (e.g. pageload) was still running, we'd restart the idle span's child span timeout (i.e. prolong its duration potentially). Is is unintended because by definition the standalone span should not be associated with a potentially ongoing idle span and its tree. This can happen, for example when users hide the page while the pageload span is still active, causing web vitals to be reported.
1 parent 61feeb3 commit 65162a2

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

packages/core/src/tracing/idleSpan.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
import { timestampInSeconds } from '../utils/time';
1818
import { freezeDscOnSpan, getDynamicSamplingContextFromSpan } from './dynamicSamplingContext';
1919
import { SentryNonRecordingSpan } from './sentryNonRecordingSpan';
20+
import { SentrySpan } from './sentrySpan';
2021
import { SPAN_STATUS_ERROR } from './spanstatus';
2122
import { startInactiveSpan } from './trace';
2223

@@ -326,7 +327,12 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
326327
// or if this is the idle span itself being started,
327328
// or if the started span has already been closed,
328329
// we don't care about it for activity
329-
if (_finished || startedSpan === span || !!spanToJSON(startedSpan).timestamp) {
330+
if (
331+
_finished ||
332+
startedSpan === span ||
333+
!!spanToJSON(startedSpan).timestamp ||
334+
(startedSpan instanceof SentrySpan && startedSpan.isStandaloneSpan())
335+
) {
330336
return;
331337
}
332338

packages/core/test/lib/tracing/idleSpan.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,28 @@ describe('startIdleSpan', () => {
586586
expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded');
587587
expect(spanToJSON(idleSpan).timestamp).toBeDefined();
588588
});
589+
590+
it("doesn't reset the timeout for standalone spans", () => {
591+
const idleSpan = startIdleSpan({ name: 'idle span' }, { finalTimeout: 99_999 });
592+
expect(idleSpan).toBeDefined();
593+
594+
// Start any span to cancel idle timeout
595+
startInactiveSpan({ name: 'span' });
596+
597+
// Wait some time
598+
vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000);
599+
expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded');
600+
expect(spanToJSON(idleSpan).timestamp).toBeUndefined();
601+
602+
// new standalone span should not reset the timeout
603+
const standaloneSpan = startInactiveSpan({ name: 'standalone span', experimental: { standalone: true } });
604+
expect(standaloneSpan).toBeDefined();
605+
606+
// Wait for timeout to exceed
607+
vi.advanceTimersByTime(1001);
608+
expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded');
609+
expect(spanToJSON(idleSpan).timestamp).toBeDefined();
610+
});
589611
});
590612

591613
describe('disableAutoFinish', () => {

0 commit comments

Comments
 (0)