Skip to content

Commit

Permalink
⚡️ [RUM-6813] Lazy load session replay (#3152)
Browse files Browse the repository at this point in the history
  • Loading branch information
amortemousque authored Dec 18, 2024
1 parent c83326b commit db2c5bd
Show file tree
Hide file tree
Showing 30 changed files with 467 additions and 193 deletions.
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ script-tests:
interruptible: true
script:
- yarn
- yarn build:bundle
- yarn test:script
########################################################################################################################
# Deploy
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/browser/runOnReadyState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@ export function runOnReadyState(
const eventName = expectedReadyState === 'complete' ? DOM_EVENT.LOAD : DOM_EVENT.DOM_CONTENT_LOADED
return addEventListener(configuration, window, eventName, callback, { once: true })
}

export function asyncRunOnReadyState(
configuration: Configuration,
expectedReadyState: 'complete' | 'interactive'
): Promise<void> {
return new Promise((resolve) => {
runOnReadyState(configuration, expectedReadyState, resolve)
})
}
4 changes: 2 additions & 2 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export {
addTelemetryUsage,
drainPreStartTelemetry,
} from './domain/telemetry'
export { monitored, monitor, callMonitored, setDebugMode } from './tools/monitor'
export { monitored, monitor, callMonitored, setDebugMode, monitorError } from './tools/monitor'
export { Observable, Subscription } from './tools/observable'
export {
startSessionManager,
Expand Down Expand Up @@ -82,7 +82,7 @@ export { AbstractLifeCycle } from './tools/abstractLifeCycle'
export * from './domain/eventRateLimiter/createEventRateLimiter'
export * from './tools/utils/browserDetection'
export { sendToExtension } from './tools/sendToExtension'
export { runOnReadyState } from './browser/runOnReadyState'
export { runOnReadyState, asyncRunOnReadyState } from './browser/runOnReadyState'
export { getZoneJsOriginalValue } from './tools/getZoneJsOriginalValue'
export { instrumentMethod, instrumentSetter, InstrumentedMethodCall } from './tools/instrumentMethod'
export {
Expand Down
18 changes: 11 additions & 7 deletions packages/core/src/tools/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,17 @@ export function callMonitored<T extends (...args: any[]) => any>(
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return fn.apply(context, args)
} catch (e) {
displayIfDebugEnabled(e)
if (onMonitorErrorCollected) {
try {
onMonitorErrorCollected(e)
} catch (e) {
displayIfDebugEnabled(e)
}
monitorError(e)
}
}

export function monitorError(e: unknown) {
displayIfDebugEnabled(e)
if (onMonitorErrorCollected) {
try {
onMonitorErrorCollected(e)
} catch (e) {
displayIfDebugEnabled(e)
}
}
}
Expand Down
1 change: 0 additions & 1 deletion packages/core/tsconfig.esm.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"module": "es6",
"rootDir": "./src/",
"outDir": "./esm/"
},
Expand Down
1 change: 0 additions & 1 deletion packages/rum-core/tsconfig.esm.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"module": "es6",
"rootDir": "./src/",
"outDir": "./esm/"
},
Expand Down
1 change: 0 additions & 1 deletion packages/rum-react/tsconfig.esm.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"module": "es6",
"rootDir": "./src/",
"outDir": "./esm/"
},
Expand Down
1 change: 0 additions & 1 deletion packages/rum-slim/tsconfig.esm.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"module": "es6",
"rootDir": "./src/",
"outDir": "./esm/"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/rum/src/boot/lazyLoadRecorder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function lazyLoadRecorder() {
return import(/* webpackChunkName: "recorder" */ './startRecording').then((module) => module.startRecording)
}
55 changes: 30 additions & 25 deletions packages/rum/src/boot/postStartStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
RumSession,
} from '@datadog/browser-rum-core'
import { LifeCycleEventType, SessionReplayState } from '@datadog/browser-rum-core'
import { PageExitReason, runOnReadyState, type DeflateEncoder } from '@datadog/browser-core'
import { asyncRunOnReadyState, monitorError, PageExitReason, type DeflateEncoder } from '@datadog/browser-core'
import { getSessionReplayLink } from '../domain/getSessionReplayLink'
import type { startRecording } from './startRecording'

Expand Down Expand Up @@ -37,10 +37,11 @@ export function createPostStartStrategy(
lifeCycle: LifeCycle,
sessionManager: RumSessionManager,
viewHistory: ViewHistory,
startRecordingImpl: StartRecording,
loadRecorder: () => Promise<StartRecording>,
getOrCreateDeflateEncoder: () => DeflateEncoder | undefined
): Strategy {
let status = RecorderStatus.Stopped
let stopRecording: () => void

lifeCycle.subscribe(LifeCycleEventType.SESSION_EXPIRED, () => {
if (status === RecorderStatus.Starting || status === RecorderStatus.Started) {
Expand All @@ -62,6 +63,30 @@ export function createPostStartStrategy(
}
})

const doStart = async () => {
const [startRecordingImpl] = await Promise.all([loadRecorder(), asyncRunOnReadyState(configuration, 'interactive')])

if (status !== RecorderStatus.Starting) {
return
}

const deflateEncoder = getOrCreateDeflateEncoder()
if (!deflateEncoder) {
status = RecorderStatus.Stopped
return
}

;({ stop: stopRecording } = startRecordingImpl(
lifeCycle,
configuration,
sessionManager,
viewHistory,
deflateEncoder
))

status = RecorderStatus.Started
}

function start(options?: StartRecordingOptions) {
const session = sessionManager.findTrackedSession()
if (canStartRecording(session, options)) {
Expand All @@ -75,42 +100,22 @@ export function createPostStartStrategy(

status = RecorderStatus.Starting

runOnReadyState(configuration, 'interactive', () => {
if (status !== RecorderStatus.Starting) {
return
}

const deflateEncoder = getOrCreateDeflateEncoder()
if (!deflateEncoder) {
status = RecorderStatus.Stopped
return
}

;({ stop: stopRecording } = startRecordingImpl(
lifeCycle,
configuration,
sessionManager,
viewHistory,
deflateEncoder
))

status = RecorderStatus.Started
})
// Intentionally not awaiting doStart() to keep it asynchronous
doStart().catch(monitorError)

if (shouldForceReplay(session!, options)) {
sessionManager.setForcedReplay()
}
}

function stop() {
if (status !== RecorderStatus.Stopped && status === RecorderStatus.Started) {
if (status === RecorderStatus.Started) {
stopRecording?.()
}

status = RecorderStatus.Stopped
}

let stopRecording: () => void
return {
start,
stop,
Expand Down
Loading

0 comments on commit db2c5bd

Please sign in to comment.