Skip to content

Commit c017181

Browse files
authored
fix(tracing): Don't send negative ttfb (#10286)
As per https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStart, `responseStart` can be 0 if the request is coming straight from the cache. This might lead us to calculate a negative ttfb. To account for these scenarios, use `Math.max` to make sure we always set to 0 in the case of a negative value.
1 parent 675309d commit c017181

File tree

2 files changed

+68
-18
lines changed

2 files changed

+68
-18
lines changed

packages/tracing-internal/src/browser/metrics/index.ts

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -240,24 +240,7 @@ export function addPerformanceEntries(transaction: Transaction): void {
240240

241241
// Measurements are only available for pageload transactions
242242
if (op === 'pageload') {
243-
// Generate TTFB (Time to First Byte), which measured as the time between the beginning of the transaction and the
244-
// start of the response in milliseconds
245-
if (typeof responseStartTimestamp === 'number' && transactionStartTime) {
246-
DEBUG_BUILD && logger.log('[Measurements] Adding TTFB');
247-
_measurements['ttfb'] = {
248-
value: (responseStartTimestamp - transactionStartTime) * 1000,
249-
unit: 'millisecond',
250-
};
251-
252-
if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) {
253-
// Capture the time spent making the request and receiving the first byte of the response.
254-
// This is the time between the start of the request and the start of the response in milliseconds.
255-
_measurements['ttfb.requestTime'] = {
256-
value: (responseStartTimestamp - requestStartTimestamp) * 1000,
257-
unit: 'millisecond',
258-
};
259-
}
260-
}
243+
_addTtfbToMeasurements(_measurements, responseStartTimestamp, requestStartTimestamp, transactionStartTime);
261244

262245
['fcp', 'fp', 'lcp'].forEach(name => {
263246
if (!_measurements[name] || !transactionStartTime || timeOrigin >= transactionStartTime) {
@@ -547,3 +530,41 @@ function setResourceEntrySizeData(
547530
data[dataKey] = entryVal;
548531
}
549532
}
533+
534+
/**
535+
* Add ttfb information to measurements
536+
*
537+
* Exported for tests
538+
*/
539+
export function _addTtfbToMeasurements(
540+
_measurements: Measurements,
541+
responseStartTimestamp: number | undefined,
542+
requestStartTimestamp: number | undefined,
543+
transactionStartTime: number | undefined,
544+
): void {
545+
// Generate TTFB (Time to First Byte), which measured as the time between the beginning of the transaction and the
546+
// start of the response in milliseconds
547+
if (typeof responseStartTimestamp === 'number' && transactionStartTime) {
548+
DEBUG_BUILD && logger.log('[Measurements] Adding TTFB');
549+
_measurements['ttfb'] = {
550+
// As per https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStart,
551+
// responseStart can be 0 if the request is coming straight from the cache.
552+
// This might lead us to calculate a negative ttfb if we don't use Math.max here.
553+
//
554+
// This logic is the same as what is in the web-vitals library to calculate ttfb
555+
// https://github.com/GoogleChrome/web-vitals/blob/2301de5015e82b09925238a228a0893635854587/src/onTTFB.ts#L92
556+
// TODO(abhi): We should use the web-vitals library instead of this custom calculation.
557+
value: Math.max(responseStartTimestamp - transactionStartTime, 0) * 1000,
558+
unit: 'millisecond',
559+
};
560+
561+
if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) {
562+
// Capture the time spent making the request and receiving the first byte of the response.
563+
// This is the time between the start of the request and the start of the response in milliseconds.
564+
_measurements['ttfb.requestTime'] = {
565+
value: (responseStartTimestamp - requestStartTimestamp) * 1000,
566+
unit: 'millisecond',
567+
};
568+
}
569+
}
570+
}

packages/tracing-internal/test/browser/metrics/index.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Transaction } from '../../../src';
22
import type { ResourceEntry } from '../../../src/browser/metrics';
3+
import { _addTtfbToMeasurements } from '../../../src/browser/metrics';
34
import { _addMeasureSpans, _addResourceSpans } from '../../../src/browser/metrics';
45
import { WINDOW } from '../../../src/browser/types';
56

@@ -261,6 +262,34 @@ describe('_addResourceSpans', () => {
261262
});
262263
});
263264

265+
describe('_addTtfbToMeasurements', () => {
266+
it('adds ttfb to measurements', () => {
267+
const measurements = {};
268+
_addTtfbToMeasurements(measurements, 300, 200, 100);
269+
expect(measurements).toEqual({
270+
ttfb: {
271+
unit: 'millisecond',
272+
value: 200000,
273+
},
274+
'ttfb.requestTime': {
275+
unit: 'millisecond',
276+
value: 100000,
277+
},
278+
});
279+
});
280+
281+
it('does not add negative ttfb', () => {
282+
const measurements = {};
283+
_addTtfbToMeasurements(measurements, 100, 200, 300);
284+
expect(measurements).toEqual({
285+
ttfb: {
286+
unit: 'millisecond',
287+
value: 0,
288+
},
289+
});
290+
});
291+
});
292+
264293
const setGlobalLocation = (location: Location) => {
265294
// @ts-expect-error need to delete this in order to set to new value
266295
delete WINDOW.location;

0 commit comments

Comments
 (0)