Skip to content
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

🐛 [RUM-99] don't stop RUM/Replay on beforeunload #3406

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
36 changes: 23 additions & 13 deletions packages/rum-core/src/domain/view/trackViews.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ describe('view lifecycle', () => {
expect(getViewCreateCount()).toBe(1)
})

it('should not end the view if the view already ended', () => {
it('should not end the view again if the view is already ended', () => {
const { getViewEndCount, getViewUpdateCount } = viewTest

lifeCycle.notify(LifeCycleEventType.PAGE_EXITED, { reason: PageExitReason.UNLOADING })
expect(getViewEndCount()).toBe(0)

lifeCycle.notify(LifeCycleEventType.SESSION_EXPIRED)

expect(getViewEndCount()).toBe(1)
expect(getViewUpdateCount()).toBe(2)
Expand Down Expand Up @@ -290,23 +292,31 @@ describe('view lifecycle', () => {

describe('page exit', () => {
;[
{ exitReason: PageExitReason.UNLOADING, expectViewEnd: true },
{ exitReason: PageExitReason.FROZEN, expectViewEnd: false },
{ exitReason: PageExitReason.HIDDEN, expectViewEnd: false },
].forEach(({ exitReason, expectViewEnd }) => {
it(`should ${
expectViewEnd ? '' : 'not '
}end the current view when the page is exiting for reason ${exitReason}`, () => {
{ exitReason: PageExitReason.UNLOADING },
{ exitReason: PageExitReason.FROZEN },
{ exitReason: PageExitReason.HIDDEN },
].forEach(({ exitReason }) => {
it(`should not end the current view when the page is exiting for reason ${exitReason}`, () => {
const { getViewEndCount } = viewTest

expect(getViewEndCount()).toEqual(0)

lifeCycle.notify(LifeCycleEventType.PAGE_EXITED, { reason: exitReason })

expect(getViewEndCount()).toEqual(expectViewEnd ? 1 : 0)
expect(getViewEndCount()).toEqual(0)
})
})

it('should set the view inactive when the page is exiting', () => {
const { getViewUpdate, getViewUpdateCount } = viewTest

expect(getViewUpdate(getViewUpdateCount() - 1).isActive).toBe(true)

lifeCycle.notify(LifeCycleEventType.PAGE_EXITED, { reason: PageExitReason.UNLOADING })

expect(getViewUpdate(getViewUpdateCount() - 1).isActive).toBe(false)
})

it('should not create a new view when ending the view on page exit', () => {
const { getViewCreateCount } = viewTest

Expand Down Expand Up @@ -839,16 +849,16 @@ describe('view event count', () => {
})

it('should take child events occurring on view end into account', () => {
viewTest = setupViewTest({ lifeCycle })
viewTest = setupViewTest({ lifeCycle, initialLocation: 'http://foo.com' })
const { getViewUpdate, getViewUpdateCount } = viewTest

lifeCycle.subscribe(LifeCycleEventType.VIEW_ENDED, () => {
lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, createFakeActionEvent())
})

lifeCycle.notify(LifeCycleEventType.PAGE_EXITED, { reason: PageExitReason.UNLOADING })
viewTest.changeLocation('/bar')

expect(getViewUpdate(getViewUpdateCount() - 1).eventCounts.actionCount).toBe(1)
expect(getViewUpdate(getViewUpdateCount() - 2).eventCounts.actionCount).toBe(1)
})

it('should be updated for 5 min after view end', () => {
Expand Down
11 changes: 8 additions & 3 deletions packages/rum-core/src/domain/view/trackViews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,9 @@ export function trackViews(
currentView.end({ sessionIsActive: false })
})

// End the current view on page unload
lifeCycle.subscribe(LifeCycleEventType.PAGE_EXITED, (pageExitEvent) => {
if (pageExitEvent.reason === PageExitReason.UNLOADING) {
currentView.end()
currentView.setInactive()
}
})
}
Expand Down Expand Up @@ -211,6 +210,7 @@ function newView(
const customTimings: ViewCustomTimings = {}
let documentVersion = 0
let endClocks: ClocksState | undefined
let isActive = true
const location = shallowClone(initialLocation)
const contextManager = createContextManager()

Expand Down Expand Up @@ -306,7 +306,7 @@ function newView(
commonViewMetrics: getCommonViewMetrics(),
initialViewMetrics,
duration: elapsed(startClocks.timeStamp, currentEnd),
isActive: endClocks === undefined,
isActive,
sessionIsActive,
eventCounts,
})
Expand All @@ -320,13 +320,18 @@ function newView(
version,
contextManager,
stopObservable,
setInactive() {
isActive = false
triggerViewUpdate()
},
end(options: { endClocks?: ClocksState; sessionIsActive?: boolean } = {}) {
if (endClocks) {
// view already ended
return
}
endClocks = options.endClocks ?? clocksNow()
sessionIsActive = options.sessionIsActive ?? true
isActive = false

lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, { endClocks })
lifeCycle.notify(LifeCycleEventType.AFTER_VIEW_ENDED, { endClocks })
Expand Down
7 changes: 0 additions & 7 deletions packages/rum/src/boot/postStartStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,6 @@ export function createPostStartStrategy(
}
})

// Stop the recorder on page unload to avoid sending records after the page is ended.
lifeCycle.subscribe(LifeCycleEventType.PAGE_EXITED, (pageExitEvent) => {
if (pageExitEvent.reason === PageExitReason.UNLOADING) {
stop()
}
})

lifeCycle.subscribe(LifeCycleEventType.SESSION_RENEWED, () => {
if (status === RecorderStatus.IntentToStart) {
start()
Expand Down
12 changes: 0 additions & 12 deletions packages/rum/src/boot/recorderApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,18 +315,6 @@ describe('makeRecorderApi', () => {
setupRecorderApi({ sessionManager })
})

// prevent getting records after the before_unload event has been triggered.
it('stop recording when the page unloads', async () => {
sessionManager.setTrackedWithSessionReplay()
rumInit()
await collectAsyncCalls(startRecordingSpy, 1)

expect(startRecordingSpy).toHaveBeenCalledTimes(1)

lifeCycle.notify(LifeCycleEventType.PAGE_EXITED, { reason: PageExitReason.UNLOADING })
expect(stopRecordingSpy).toHaveBeenCalled()
})

describe('when session renewal change the tracking type', () => {
describe('from WITHOUT_REPLAY to WITH_REPLAY', () => {
beforeEach(() => {
Expand Down