Skip to content

Commit

Permalink
Decouple longest interactions management
Browse files Browse the repository at this point in the history
  • Loading branch information
amortemousque committed Jul 28, 2023
1 parent f3bcc07 commit 1bd912a
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 45 deletions.
1 change: 0 additions & 1 deletion packages/rum-core/src/browser/performanceCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,6 @@ function retrieveFirstInputTiming(callback: (timing: RumFirstInputTiming) => voi
entryType: 'first-input',
processingStart: relativeNow(),
startTime: evt.timeStamp as RelativeTime,
duration: 0 as Duration,
}

if (evt.type === DOM_EVENT.POINTER_DOWN) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,75 +24,78 @@ export function trackInteractionToNextPaint(viewLoadingType: ViewLoadingType, li
}
}

// List of longest interactions on the view sorted by descending duration.
const longestInteractions: RumEventTiming[] = []

const { getViewInteractionCount } = trackViewInteractionCount(viewLoadingType)
const longestInteractions = trackLongestInteractions(getViewInteractionCount)
let maxInpDuration = -1 as Duration

const { unsubscribe: stop } = lifeCycle.subscribe(LifeCycleEventType.PERFORMANCE_ENTRIES_COLLECTED, (entries) => {
for (const entry of entries) {
if (entry.entryType === 'event' && entry.interactionId) {
processEntry(entry)
longestInteractions.process(entry)
}
}

const inp = estimateP98LongestInteraction(getViewInteractionCount)
const inp = longestInteractions.estimateP98Duration()
if (inp && inp.duration > maxInpDuration) {
maxInpDuration = inp.duration
}
})

/**
* Process the performance entry:
* - if its duration is long enough, add the performance entry to the list of worst interactions
* - if an entry with the same interaction id exists and its duration is lower than the new one, then replace it in the list of worst interactions
*/
function processEntry(entry: RumEventTiming) {
const interactionIndex = longestInteractions.findIndex(
(interaction) => entry.interactionId === interaction.interactionId
)

const minLongestInteraction = longestInteractions[longestInteractions.length - 1]

if (interactionIndex !== -1) {
if (entry.duration > longestInteractions[interactionIndex].duration) {
longestInteractions[interactionIndex] = entry
sortAndTrimLongestInteractions()
return {
getInteractionToNextPaint: () => {
// If no INP duration where captured because of the performanceObserver 40ms threshold
// but the view interaction count > 0 then report 0
if (maxInpDuration === -1 && getViewInteractionCount()) {
return 0 as Duration
} else if (maxInpDuration >= 0) {
return maxInpDuration
}
} else if (
longestInteractions.length < MAX_INTERACTION_ENTRIES ||
entry.duration > minLongestInteraction.duration
) {
longestInteractions.push(entry)
sortAndTrimLongestInteractions()
}
},
stop,
}
}

/**
* Compute the p98 longest interaction.
* For better performance the computation is based on 10 longest interactions and the interaction count of the current view.
*/
function estimateP98LongestInteraction(getViewInteractionCount: () => number) {
const interactionIndex = Math.min(longestInteractions.length - 1, Math.floor(getViewInteractionCount() / 50))
return longestInteractions[interactionIndex]
}
function trackLongestInteractions(getViewInteractionCount: () => number) {
const longestInteractions: RumEventTiming[] = []

function sortAndTrimLongestInteractions() {
longestInteractions.sort((a, b) => b.duration - a.duration).splice(MAX_INTERACTION_ENTRIES)
}

return {
getInteractionToNextPaint: () => {
// If the interaction count shows there were interactions but
// none were captured by the PerformanceObserver because of the threshold, report a latency of 0.
if (maxInpDuration === -1 && getViewInteractionCount()) {
return 0 as Duration
} else if (maxInpDuration >= 0) {
return maxInpDuration
/**
* Process the performance entry:
* - if its duration is long enough, add the performance entry to the list of worst interactions
* - if an entry with the same interaction id exists and its duration is lower than the new one, then replace it in the list of worst interactions
*/
process(entry: RumEventTiming) {
const interactionIndex = longestInteractions.findIndex(
(interaction) => entry.interactionId === interaction.interactionId
)

const minLongestInteraction = longestInteractions[longestInteractions.length - 1]

if (interactionIndex !== -1) {
if (entry.duration > longestInteractions[interactionIndex].duration) {
longestInteractions[interactionIndex] = entry
sortAndTrimLongestInteractions()
}
} else if (
longestInteractions.length < MAX_INTERACTION_ENTRIES ||
entry.duration > minLongestInteraction.duration
) {
longestInteractions.push(entry)
sortAndTrimLongestInteractions()
}
},
stop,
/**
* Compute the p98 longest interaction.
* For better performance the computation is based on 10 longest interactions and the interaction count of the current view.
*/
estimateP98Duration() {
const interactionIndex = Math.min(longestInteractions.length - 1, Math.floor(getViewInteractionCount() / 50))
return longestInteractions[interactionIndex]
},
}
}

Expand Down

0 comments on commit 1bd912a

Please sign in to comment.