Skip to content

Commit

Permalink
--wip-- [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
N-Boutaib committed Sep 27, 2024
1 parent f057580 commit 243b4b7
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 61 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"postinstall": "scripts/cli init_submodule",
"build": "lerna run build --stream",
"build:bundle": "lerna run build:bundle --stream",
"format": "prettier --check .",
"format": "prettier --write .",
"lint": "scripts/cli lint .",
"typecheck": "scripts/cli typecheck . && scripts/cli typecheck developer-extension",
"dev": "node scripts/dev-server.js",
Expand Down
1 change: 1 addition & 0 deletions packages/rum/src/boot/startRecording.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function startRecording(

flushCachedRecords = () => {
;({ addRecord } = initSegmentCollection())
cacheInitResult.squashCachedRecords()
const records = cacheInitResult.getRecords()
records.forEach((record: BrowserRecord) => addRecord(record))
}
Expand Down
2 changes: 1 addition & 1 deletion packages/rum/src/domain/record/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { record } from './record'
export { serializeNodeWithId, serializeDocument, SerializationContextStatus, } from './serialization'
export { serializeNodeWithId, serializeDocument, SerializationContextStatus } from './serialization'
export { createElementsScrollPositions } from './elementsScrollPositions'
export { ShadowRootsController } from './shadowRootsController'
export { getSerialisedNodeMap } from './serialization'
38 changes: 35 additions & 3 deletions packages/rum/src/domain/recordsCaching/recordsCaching.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,50 @@
import { timeStampNow } from '@datadog/browser-core'
import type { TimeStamp } from '@datadog/browser-core'
import type { BrowserRecord } from '../../types'
import { squashRecords } from '../squashing'

const SQUASHING_INTERVAL = 15_000

export function startRecordsCaching() {
const records: BrowserRecord[] = []
let cachedRecords: BrowserRecord[] = []

function addRecord(record: BrowserRecord) {
records.push(record)
cachedRecords.push(record)
}

function getRecords() {
return records
return cachedRecords
}

function squashCachedRecords() {
const squashingTimestamp = (timeStampNow() - SQUASHING_INTERVAL) as TimeStamp
const [recordsBeforeSeekTs, recordsAfterSeekTs] = partition(
cachedRecords,
(r: BrowserRecord) => r.timestamp <= squashingTimestamp
)

const squashedRecords = squashRecords(recordsBeforeSeekTs, squashingTimestamp)
cachedRecords = squashedRecords.concat(recordsAfterSeekTs)
}

return {
addRecord,
getRecords,
squashCachedRecords,
}
}

function partition<T>(array: T[], predicate: (value: T) => boolean): [T[], T[]] {
const truthy: T[] = []
const falsy: T[] = []

array.forEach((item) => {
if (predicate(item)) {
truthy.push(item)
} else {
falsy.push(item)
}
})

return [truthy, falsy]
}
4 changes: 1 addition & 3 deletions packages/rum/src/domain/squashing/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
export * from './applyDomMutation'
export * from './applyInputMutation'
export * from './applyScrollMutation'
export * from './squashRecords'
231 changes: 189 additions & 42 deletions packages/rum/src/domain/squashing/squashRecords.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,192 @@
import type { BrowserRecord } from '../../types';
import { IncrementalSource, RecordType } from '../../types';
import { getSerialisedNodeMap } from '../record';
import { applyDomMutation } from './applyDomMutation';
import { applyInputMutation } from './applyInputMutation';
import { applyScrollMutation } from './applyScrollMutation';

export function squashRecords(records: BrowserRecord[], squashingTimestamp: number): BrowserRecord[] {
const serializedNodeMap = getSerialisedNodeMap()
const squashedRecords: BrowserRecord[] = []

// Find FuulSnapshotRecord
const fullsnapshotRecord = records.find((record) => record.type === RecordType.FullSnapshot)!
// Find MetaRecord
const metaRecord = records.find((record) => record.type === RecordType.Meta)!
// Find Last FocusRecord
const focusRecord = records.find((record) => record.type === RecordType.Focus)!
// Find VisualViewportRecord
const visualViewportRecord = records.find((record) => record.type === RecordType.VisualViewport)!
for (const record of records) {
switch (record.type) {
case RecordType.IncrementalSnapshot:
switch (record.data.source) {
case IncrementalSource.MouseInteraction:
case IncrementalSource.TouchMove:
squashedRecords.push(record)
break
case IncrementalSource.Mutation:
applyDomMutation(serializedNodeMap, record.data)
break
case IncrementalSource.Input:
applyInputMutation(record.data, serializedNodeMap)
break
case IncrementalSource.Scroll:
applyScrollMutation(record.data, serializedNodeMap, fullsnapshotRecord)
break
default:
break
}
import { assign, find } from '@datadog/browser-core'
import type { TimeStamp } from '@datadog/browser-core'
import type {
BrowserFullSnapshotRecord,
BrowserRecord,
FocusRecord,
MetaRecord,
VisualViewportRecord,
} from '../../types'
import { IncrementalSource, RecordType } from '../../types'
import { getSerialisedNodeMap } from '../record'
import { applyDomMutation } from './applyDomMutation'
import { applyInputMutation } from './applyInputMutation'
import { applyScrollMutation } from './applyScrollMutation'

interface SqaushingMutatedRecords {
fullsnapshotRecord: BrowserFullSnapshotRecord
metaRecord: MetaRecord
focusRecord: FocusRecord
visualViewportRecord?: VisualViewportRecord
frustrationRecord?: BrowserRecord
cssRulesRecords?: BrowserRecord[]
mediaInteractionRecords?: BrowserRecord[]
mouseHoverRecord?: BrowserRecord
mouseFocusRecord?: BrowserRecord
mousePositionRecord?: BrowserRecord
}

export function squashRecords(records: BrowserRecord[], squashingTimestamp: TimeStamp): BrowserRecord[] {
// If any of the basic records is missing, return the original records
if (records.length === 0) {
return records
}

const serializedNodeMap = getSerialisedNodeMap()
const squashingMutatedRecords: SqaushingMutatedRecords = {
fullsnapshotRecord: find(records, (record) => record.type === RecordType.FullSnapshot)!,
metaRecord: find(records, (record) => record.type === RecordType.Meta)!,
focusRecord: find(records, (record) => record.type === RecordType.Focus)!,
visualViewportRecord: find(records, (record) => record.type === RecordType.VisualViewport),
}

const unsquashedRecords: BrowserRecord[] = []

for (const record of records) {
switch (record.type) {
case RecordType.Meta:
break
case RecordType.FullSnapshot:
break
case RecordType.Focus:
if (record !== squashingMutatedRecords.focusRecord) {
squashingMutatedRecords.focusRecord = record
}
break
case RecordType.VisualViewport:
// Maybe we should update MetaRecord width & height
if (record !== squashingMutatedRecords.visualViewportRecord) {
squashingMutatedRecords.visualViewportRecord = record
}
break
case RecordType.IncrementalSnapshot:
switch (record.data.source) {
case IncrementalSource.Mutation:
applyDomMutation(serializedNodeMap, record.data)
break
case IncrementalSource.Input:
applyInputMutation(record.data, serializedNodeMap)
break
case IncrementalSource.Scroll:
applyScrollMutation(record.data, serializedNodeMap, squashingMutatedRecords.fullsnapshotRecord)
break
case IncrementalSource.StyleSheetRule:
if (!squashingMutatedRecords.cssRulesRecords) {
squashingMutatedRecords.cssRulesRecords = [record]
} else {
squashingMutatedRecords.cssRulesRecords.push(record)
}
break
case IncrementalSource.MediaInteraction:
if (!squashingMutatedRecords.mediaInteractionRecords) {
squashingMutatedRecords.mediaInteractionRecords = [record]
} else {
squashingMutatedRecords.mediaInteractionRecords.push(record)
}
break
case IncrementalSource.ViewportResize:
// Update MetaRecord width & height
squashingMutatedRecords.metaRecord.data.height = record.data.height
squashingMutatedRecords.metaRecord.data.width = record.data.width
// Discard VisualViewportRecord
delete squashingMutatedRecords.visualViewportRecord
break
default:
unsquashedRecords.push(record)
break
}
break
default:
unsquashedRecords.push(record)
break
}
}

// Update timings of records (add -50 ms) to give timeto unsquashed records
return finaliseSquashing(squashingMutatedRecords, unsquashedRecords, squashingTimestamp)
}

function finaliseStyleRulesSquashing(cssRulesRecords?: BrowserRecord[]): BrowserRecord | undefined {
if (!cssRulesRecords) {
return
}
const adds = []
const removes = []
let lastRecord
for (const record of cssRulesRecords) {
if (record.type !== RecordType.IncrementalSnapshot || record.data.source !== IncrementalSource.StyleSheetRule) {
continue
}
if (record.data.adds) {
adds.push(...record.data.adds)
}
if (record.data.removes) {
removes.push(...record.data.removes)
}
lastRecord = record
}

if (lastRecord) {
if (
lastRecord.type !== RecordType.IncrementalSnapshot ||
lastRecord.data.source !== IncrementalSource.StyleSheetRule
) {
return
}
lastRecord.data.adds = adds
lastRecord.data.removes = removes
}

return lastRecord
}

function finaliseMediaInteractionSquashing(mediaInteractionRecords?: BrowserRecord[]): BrowserRecord | undefined {
if (!mediaInteractionRecords) {
return
}
const lastRecord = mediaInteractionRecords[mediaInteractionRecords.length - 1]
if (
lastRecord.type !== RecordType.IncrementalSnapshot ||
lastRecord.data.source !== IncrementalSource.MediaInteraction
) {
return
}
if (lastRecord.data.type === 1) {
return lastRecord
}
}

function finaliseSquashing(
state: SqaushingMutatedRecords,
unsquashedRecords: BrowserRecord[],
timestamp: TimeStamp
): BrowserRecord[] {
const squashedRecords: BrowserRecord[] = []
const styleSheetRuleRecord = finaliseStyleRulesSquashing(state.cssRulesRecords)
const mediaInteractionRecord = finaliseMediaInteractionSquashing(state.mediaInteractionRecords)

squashedRecords.push(assign(state.metaRecord, { timestamp }))
squashedRecords.push(assign(state.focusRecord, { timestamp }))
squashedRecords.push(assign(state.fullsnapshotRecord, { timestamp }))
if (state.visualViewportRecord) {
squashedRecords.push(assign(state.visualViewportRecord, { timestamp }))
}

if (styleSheetRuleRecord) {
squashedRecords.push(assign(styleSheetRuleRecord, { timestamp }))
}
if (mediaInteractionRecord) {
squashedRecords.push(assign(mediaInteractionRecord, { timestamp }))
}

squashedRecords
.concat(unsquashedRecords)
.sort((r1, r2) => r1.timestamp - r2.timestamp)
.forEach((record) => {
record.timestamp = timestamp
})

console.log('SQUASHING RESULT', squashedRecords)

// Update timings of records (add -50 ms) to give timeto unsquashed records
return squashedRecords;
}
return squashedRecords
}
Loading

0 comments on commit 243b4b7

Please sign in to comment.