Skip to content

Commit

Permalink
πŸ› [RUMF-900] clear parent view context when view end (#816)
Browse files Browse the repository at this point in the history
* πŸ› clear parent view context when view end

prevent events to be attached to the view after its end

* πŸ‘Œ no need to check view id on VIEW_END
  • Loading branch information
bcaudan authored Apr 29, 2021
1 parent 6bc5546 commit 270cff1
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 20 deletions.
6 changes: 3 additions & 3 deletions packages/rum-core/src/domain/lifeCycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CommonContext, RawRumEvent } from '../rawRumEvent.types'
import { RumEvent } from '../rumEvent.types'
import { RequestCompleteEvent, RequestStartEvent } from './requestCollection'
import { AutoAction, AutoActionCreatedEvent } from './rumEventsCollection/action/trackActions'
import { ViewEvent, ViewCreatedEvent } from './rumEventsCollection/view/trackViews'
import { ViewEvent, ViewCreatedEvent, ViewEndedEvent } from './rumEventsCollection/view/trackViews'

export enum LifeCycleEventType {
PERFORMANCE_ENTRY_COLLECTED,
Expand Down Expand Up @@ -39,13 +39,13 @@ export class LifeCycle {
notify(eventType: LifeCycleEventType.AUTO_ACTION_CREATED, data: AutoActionCreatedEvent): void
notify(eventType: LifeCycleEventType.VIEW_CREATED, data: ViewCreatedEvent): void
notify(eventType: LifeCycleEventType.VIEW_UPDATED, data: ViewEvent): void
notify(eventType: LifeCycleEventType.VIEW_ENDED, data: ViewEndedEvent): void
notify(
eventType:
| LifeCycleEventType.SESSION_RENEWED
| LifeCycleEventType.DOM_MUTATED
| LifeCycleEventType.BEFORE_UNLOAD
| LifeCycleEventType.AUTO_ACTION_DISCARDED
| LifeCycleEventType.VIEW_ENDED
| LifeCycleEventType.RECORD_STARTED
| LifeCycleEventType.RECORD_STOPPED
): void
Expand Down Expand Up @@ -82,13 +82,13 @@ export class LifeCycle {
): Subscription
subscribe(eventType: LifeCycleEventType.VIEW_CREATED, callback: (data: ViewCreatedEvent) => void): Subscription
subscribe(eventType: LifeCycleEventType.VIEW_UPDATED, callback: (data: ViewEvent) => void): Subscription
subscribe(eventType: LifeCycleEventType.VIEW_ENDED, callback: (data: ViewEndedEvent) => void): Subscription
subscribe(
eventType:
| LifeCycleEventType.SESSION_RENEWED
| LifeCycleEventType.DOM_MUTATED
| LifeCycleEventType.BEFORE_UNLOAD
| LifeCycleEventType.AUTO_ACTION_DISCARDED
| LifeCycleEventType.VIEW_ENDED
| LifeCycleEventType.RECORD_STARTED
| LifeCycleEventType.RECORD_STOPPED,
callback: () => void
Expand Down
12 changes: 11 additions & 1 deletion packages/rum-core/src/domain/parentContexts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,14 @@ describe('parentContexts', () => {
LifeCycleEventType.VIEW_CREATED,
buildViewCreatedEvent({ startClocks: relativeToClocks(10 as RelativeTime), id: 'view 1' })
)
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, { endClocks: relativeToClocks(20 as RelativeTime) })

lifeCycle.notify(
LifeCycleEventType.VIEW_CREATED,
buildViewCreatedEvent({ startClocks: relativeToClocks(20 as RelativeTime), id: 'view 2' })
)
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, { endClocks: relativeToClocks(30 as RelativeTime) })

lifeCycle.notify(
LifeCycleEventType.VIEW_CREATED,
buildViewCreatedEvent({ startClocks: relativeToClocks(30 as RelativeTime), id: 'view 3' })
Expand All @@ -97,15 +101,17 @@ describe('parentContexts', () => {
LifeCycleEventType.VIEW_CREATED,
buildViewCreatedEvent({ startClocks: relativeToClocks(10 as RelativeTime), id: 'view 1' })
)
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, { endClocks: relativeToClocks(20 as RelativeTime) })
lifeCycle.notify(
LifeCycleEventType.VIEW_CREATED,
buildViewCreatedEvent({ startClocks: relativeToClocks(20 as RelativeTime), id: 'view 2' })
)
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, { endClocks: relativeToClocks(20 as RelativeTime) })

expect(parentContexts.findView(5 as RelativeTime)).not.toBeDefined()
})

it('should replace the current view context on VIEW_CREATED', () => {
it('should set the current view context on VIEW_CREATED', () => {
const { lifeCycle } = setupBuilder.build()

lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, buildViewCreatedEvent())
Expand Down Expand Up @@ -236,6 +242,7 @@ describe('parentContexts', () => {
startClocks: relativeToClocks(10 as RelativeTime),
})
)
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, { endClocks: relativeToClocks(20 as RelativeTime) })
lifeCycle.notify(
LifeCycleEventType.VIEW_CREATED,
buildViewCreatedEvent({
Expand Down Expand Up @@ -280,6 +287,9 @@ describe('parentContexts', () => {
startClocks: originalClocks,
})
)
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, {
endClocks: relativeToClocks((originalTime + 10) as RelativeTime),
})
lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, {
startClocks: originalClocks,
id: 'action 1',
Expand Down
20 changes: 12 additions & 8 deletions packages/rum-core/src/domain/parentContexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,29 @@ export function startParentContexts(lifeCycle: LifeCycle, session: RumSession):
let previousActions: Array<PreviousContext<ActionContext>> = []

lifeCycle.subscribe(LifeCycleEventType.VIEW_CREATED, (currentContext) => {
if (currentView) {
previousViews.unshift({
context: buildCurrentViewContext(),
endTime: currentContext.startClocks.relative,
startTime: currentView.startClocks.relative,
})
}
currentView = currentContext
currentSessionId = session.getId()
})

lifeCycle.subscribe(LifeCycleEventType.VIEW_UPDATED, (currentContext) => {
// A view can be updated after its end. We have to ensure that the view being updated is the
// most recently created.
if (currentView!.id === currentContext.id) {
if (currentView && currentView.id === currentContext.id) {
currentView = currentContext
}
})

lifeCycle.subscribe(LifeCycleEventType.VIEW_ENDED, ({ endClocks }) => {
if (currentView) {
previousViews.unshift({
endTime: endClocks.relative,
context: buildCurrentViewContext(),
startTime: currentView.startClocks.relative,
})
currentView = undefined
}
})

lifeCycle.subscribe(LifeCycleEventType.AUTO_ACTION_CREATED, (currentContext) => {
currentAction = currentContext
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {
ClocksState,
clocksNow,
preferredClock,
preferredNow,
PreferredTime,
clocksOrigin,
} from '@datadog/browser-core'
import { ViewLoadingType, ViewCustomTimings } from '../../../rawRumEvent.types'
Expand Down Expand Up @@ -46,6 +44,10 @@ export interface ViewCreatedEvent {
startClocks: ClocksState
}

export interface ViewEndedEvent {
endClocks: ClocksState
}

export const THROTTLE_VIEW_UPDATE_PERIOD = 3000
export const SESSION_KEEP_ALIVE_INTERVAL = 5 * ONE_MINUTE

Expand Down Expand Up @@ -144,7 +146,7 @@ function newView(
let timings: Timings = {}
const customTimings: ViewCustomTimings = {}
let documentVersion = 0
let endTime: PreferredTime | undefined
let endClocks: ClocksState | undefined
let location: Location = { ...initialLocation }
let hasReplay = initialHasReplay

Expand Down Expand Up @@ -182,17 +184,17 @@ function newView(
referrer,
startClocks,
timings,
duration: elapsed(preferredClock(startClocks), endTime === undefined ? preferredNow() : endTime),
isActive: endTime === undefined,
duration: elapsed(preferredClock(startClocks), preferredClock(endClocks === undefined ? clocksNow() : endClocks)),
isActive: endClocks === undefined,
})
}

return {
scheduleUpdate: scheduleViewUpdate,
end() {
endTime = preferredNow()
endClocks = clocksNow()
stopViewMetricsTracking()
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED)
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, { endClocks })
},
getLocation() {
return location
Expand Down
2 changes: 1 addition & 1 deletion packages/rum-recorder/src/boot/recorder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ describe('startRecording', () => {
it('adds a ViewEnd snapshot when the view ends', (done) => {
const { lifeCycle } = setupBuilder.build()

lifeCycle.notify(LifeCycleEventType.VIEW_ENDED)
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, {} as any)
viewId = 'view-id-2'
lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, {} as any)
flushSegment(lifeCycle)
Expand Down

0 comments on commit 270cff1

Please sign in to comment.