Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/rum/src/domain/profiling/profiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,35 @@ describe('profiler', () => {
expect(profiler.isStopped()).toBe(true)
})

it('should not restart profiling on session renewal if user called stop after session expiration', async () => {
const { profiler, profilingContextManager } = setupProfiler()

profiler.start()

// Wait for start of collection.
await waitForBoolean(() => profiler.isRunning())

expect(profilingContextManager.get()?.status).toBe('running')

// Session expires (sync - state changes immediately)
lifeCycle.notify(LifeCycleEventType.SESSION_EXPIRED)

expect(profiler.isStopped()).toBe(true)
expect(profilingContextManager.get()?.status).toBe('stopped')

// User explicitly stops the profiler after session expiration
profiler.stop()

expect(profiler.isStopped()).toBe(true)

// Session is renewed — start() is called synchronously, so no need to wait
lifeCycle.notify(LifeCycleEventType.SESSION_RENEWED)

// Profiler should remain stopped - user's explicit stop should take priority over session expiration
expect(profiler.isStopped()).toBe(true)
expect(profilingContextManager.get()?.status).toBe('stopped')
})

it('should restart profiling when session expires while paused and then renews', async () => {
const { profiler, profilingContextManager } = setupProfiler()

Expand Down
18 changes: 12 additions & 6 deletions packages/rum/src/domain/profiling/profiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ export function createRumProfiler(
// Stop current profiler instance (data collection happens async in background)
stopProfilerInstance(reason)

// Cleanup global listeners
// Cleanup global listeners and reset the array to prevent accumulation across start/stop cycles
globalCleanupTasks.forEach((task) => task())
globalCleanupTasks.length = 0

// Update Profiling status once the Profiler has been stopped.
profilingContextManager.set({ status: 'stopped', error_reason: undefined })
Expand Down Expand Up @@ -254,12 +255,17 @@ export function createRumProfiler(
}

function stopProfilerInstance(stateReason: RumProfilerStoppedInstance['stateReason']) {
if (instance.state === 'paused') {
// If paused, profiler data was already collected during pause, just update state
instance = { state: 'stopped', stateReason }
return
}
if (instance.state !== 'running') {
if (
// If paused, profiler data was already collected during pause, just update state
instance.state === 'paused' ||
// Update stateReason when already stopped and the user explicitly stops the profiler,
// so that SESSION_RENEWED does not override the user's intent.
(instance.state === 'stopped' && stateReason === 'stopped-by-user')
) {
instance = { state: 'stopped', stateReason }
}

return
}
Copy link
Member

Choose a reason for hiding this comment

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

nitpick (non-blocking): This could be slightly refactored I think:

if (instance.state !== "running") {
  if (
    // If paused, profiler data was already collected during pause, just update state
    instance.state === "paused" ||
    // Update stateReason when already stopped and the user explicitly stops the profiler,
    // so that SESSION_RENEWED does not override the user's intent.
    (instance.state === "stopped" && stateReason === "stopped-by-user")
  ) {
    instance = { state: "stopped", stateReason };
  }

  return;
}

Copy link
Collaborator Author

@thomasbertet thomasbertet Feb 19, 2026

Choose a reason for hiding this comment

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

Done in 9b7dcb8 — merged both branches into a single if (instance.state !== 'running') guard.


Expand Down