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

🐛 [RUMF-71] do not report negative performance timing duration #264

Merged
merged 3 commits into from
Feb 12, 2020
Merged
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
73 changes: 28 additions & 45 deletions packages/rum/src/resourceUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,57 +39,40 @@ export function computeResourceKind(timing: PerformanceResourceTiming) {
return ResourceKind.OTHER
}

const durationPairs: Array<[keyof PerformanceResourceTiming, keyof PerformanceResourceTiming]> = [
['connectStart', 'connectEnd'],
['domainLookupStart', 'domainLookupEnd'],
['responseStart', 'responseEnd'],
['requestStart', 'responseStart'],
['redirectStart', 'redirectEnd'],
['secureConnectionStart', 'connectEnd'],
]
function reporteAbnormalEntry(entry: PerformanceResourceTiming) {
let error = ''
for (const [start, end] of durationPairs) {
if (entry[start] < 0) {
error += `${start} is negative\n`
} else if (entry[start] > entry[end]) {
error += `${start} is greater than ${end}\n`
}
}
if (error) {
addMonitoringMessage(`Got an abnormal PerformanceResourceTiming:
${error}
Entry: ${JSON.stringify(entry)}`)
}
function formatTiming(start: number, end: number) {
return { duration: msToNs(end - start), start: msToNs(start) }
}

function isValidTiming(start: number, end: number) {
return start > 0 && end > 0 && end >= start
}

export function computePerformanceResourceDetails(
entry?: PerformanceResourceTiming
): PerformanceResourceDetails | undefined {
if (entry && hasTimingAllowedAttributes(entry)) {
reporteAbnormalEntry(entry)
return {
connect: { duration: msToNs(entry.connectEnd - entry.connectStart), start: msToNs(entry.connectStart) },
dns: {
duration: msToNs(entry.domainLookupEnd - entry.domainLookupStart),
start: msToNs(entry.domainLookupStart),
},
download: { duration: msToNs(entry.responseEnd - entry.responseStart), start: msToNs(entry.responseStart) },
firstByte: { duration: msToNs(entry.responseStart - entry.requestStart), start: msToNs(entry.requestStart) },
redirect:
entry.redirectStart > 0
? { duration: msToNs(entry.redirectEnd - entry.redirectStart), start: msToNs(entry.redirectStart) }
: undefined,
ssl:
entry.secureConnectionStart > 0
? {
duration: msToNs(entry.connectEnd - entry.secureConnectionStart),
start: msToNs(entry.secureConnectionStart),
}
: undefined,
}
if (!entry || !hasTimingAllowedAttributes(entry)) {
return undefined
}
if (
!isValidTiming(entry.connectStart, entry.connectEnd) ||
!isValidTiming(entry.domainLookupStart, entry.domainLookupEnd) ||
!isValidTiming(entry.responseStart, entry.responseEnd) ||
!isValidTiming(entry.requestStart, entry.responseStart)
) {
return undefined
}
return {
connect: formatTiming(entry.connectStart, entry.connectEnd),
dns: formatTiming(entry.domainLookupStart, entry.domainLookupEnd),
download: formatTiming(entry.responseStart, entry.responseEnd),
firstByte: formatTiming(entry.requestStart, entry.responseStart),
redirect: isValidTiming(entry.redirectStart, entry.redirectEnd)
? formatTiming(entry.redirectStart, entry.redirectEnd)
: undefined,
ssl: isValidTiming(entry.secureConnectionStart, entry.connectEnd)
? formatTiming(entry.secureConnectionStart, entry.connectEnd)
: undefined,
}
return undefined
}

export function computeSize(entry?: PerformanceResourceTiming) {
Expand Down
47 changes: 45 additions & 2 deletions packages/rum/test/rum.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,11 @@ describe('rum handle performance entry', () => {
const entry: Partial<PerformanceResourceTiming> = {
connectEnd: 10,
connectStart: 3,
domainLookupEnd: 3,
domainLookupStart: 3,
entryType: 'resource',
name: 'http://localhost/test',
requestStart: 20,
responseEnd: 100,
responseStart: 25,
}
Expand All @@ -163,8 +166,48 @@ describe('rum handle performance entry', () => {
new LifeCycle()
)
const resourceEvent = getEntry(addRumEvent, 0) as RumResourceEvent
expect(resourceEvent.http.performance!.connect.duration).toEqual(7 * 1e6)
expect(resourceEvent.http.performance!.download.duration).toEqual(75 * 1e6)
expect(resourceEvent.http.performance!.connect!.duration).toEqual(7 * 1e6)
expect(resourceEvent.http.performance!.download!.duration).toEqual(75 * 1e6)
})

describe('ignore invalid performance entry', () => {
it('when it has a negative timing start', () => {
const entry: Partial<PerformanceResourceTiming> = {
connectEnd: 10,
connectStart: -3,
entryType: 'resource',
name: 'http://localhost/test',
responseEnd: 100,
responseStart: 25,
}

handleResourceEntry(
configuration as Configuration,
entry as PerformanceResourceTiming,
addRumEvent,
new LifeCycle()
)
const resourceEvent = getEntry(addRumEvent, 0) as RumResourceEvent
expect(resourceEvent.http.performance).toBe(undefined)
})

it('when it has timing start after its end', () => {
const entry: Partial<PerformanceResourceTiming> = {
entryType: 'resource',
name: 'http://localhost/test',
responseEnd: 25,
responseStart: 100,
}

handleResourceEntry(
configuration as Configuration,
entry as PerformanceResourceTiming,
addRumEvent,
new LifeCycle()
)
const resourceEvent = getEntry(addRumEvent, 0) as RumResourceEvent
expect(resourceEvent.http.performance).toBe(undefined)
})
})
})

Expand Down
2 changes: 1 addition & 1 deletion test/e2e/scenario/agents.scenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ describe('rum', () => {
expect(timing.http.method).toEqual('GET')
expect((timing.http as any).status_code).toEqual(200)
expect(timing.duration).toBeGreaterThan(0)
expect(timing.http.performance!.download.start).toBeGreaterThan(0)
expect(timing.http.performance!.download!.start).toBeGreaterThan(0)
})

it('should send performance timings along the view events', async () => {
Expand Down