From 790e16ad849af7b5c2c41c0c0df3f1492b90d04b Mon Sep 17 00:00:00 2001 From: Chris Sauve Date: Fri, 1 Dec 2023 11:44:11 -0500 Subject: [PATCH] Add special performance event handling for time to last byte (#2698) --- .changeset/pretty-timers-begin.md | 6 +++ packages/koa-performance/src/enums.ts | 1 + packages/koa-performance/src/middleware.ts | 1 + packages/performance/src/performance.ts | 52 ++++++++++++++-------- packages/performance/src/types.ts | 10 +++++ 5 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 .changeset/pretty-timers-begin.md diff --git a/.changeset/pretty-timers-begin.md b/.changeset/pretty-timers-begin.md new file mode 100644 index 0000000000..59dd3d940d --- /dev/null +++ b/.changeset/pretty-timers-begin.md @@ -0,0 +1,6 @@ +--- +'@shopify/koa-performance': minor +'@shopify/performance': minor +--- + +Add special performance event handling for time to last byte diff --git a/packages/koa-performance/src/enums.ts b/packages/koa-performance/src/enums.ts index a97b490b12..2ddad3c904 100644 --- a/packages/koa-performance/src/enums.ts +++ b/packages/koa-performance/src/enums.ts @@ -1,5 +1,6 @@ export enum LifecycleMetric { TimeToFirstByte = 'time_to_first_byte', + TimeToLastByte = 'time_to_last_byte', TimeToFirstContentfulPaint = 'time_to_first_contentful_paint', TimeToLargestContentfulPaint = 'time_to_largest_contentful_paint', TimeToFirstPaint = 'time_to_first_paint', diff --git a/packages/koa-performance/src/middleware.ts b/packages/koa-performance/src/middleware.ts index e3472d18a0..879b9e0ffd 100644 --- a/packages/koa-performance/src/middleware.ts +++ b/packages/koa-performance/src/middleware.ts @@ -254,6 +254,7 @@ function getAnomalousNavigationDurationTag( const EVENT_METRIC_MAPPING = { [EventType.TimeToFirstByte]: LifecycleMetric.TimeToFirstByte, + [EventType.TimeToLastByte]: LifecycleMetric.TimeToLastByte, [EventType.TimeToFirstContentfulPaint]: LifecycleMetric.TimeToFirstContentfulPaint, [EventType.TimeToLargestContentfulPaint]: diff --git a/packages/performance/src/performance.ts b/packages/performance/src/performance.ts index bb595970b0..91b099ef7d 100644 --- a/packages/performance/src/performance.ts +++ b/packages/performance/src/performance.ts @@ -62,27 +62,43 @@ export class Performance { this.supportsTimingEntries && (!this.supportsDetailedTime || !this.supportsNavigationEntries) ) { - withTiming(({domContentLoadedEventStart, loadEventStart}) => { - // window.performance.timing uses full timestamps, while - // the ones coming from observing navigation entries are - // time from performance.timeOrigin. We just normalize these - // ones to be relative to "start" since things listening for - // events expect them to be relative to when the navigation - // began. - this.lifecycleEvent({ - type: EventType.DomContentLoaded, - start: domContentLoadedEventStart - this.timeOrigin, - duration: 0, - }); + withTiming( + ({responseEnd, domContentLoadedEventStart, loadEventStart}) => { + // window.performance.timing uses full timestamps, while + // the ones coming from observing navigation entries are + // time from performance.timeOrigin. We just normalize these + // ones to be relative to "start" since things listening for + // events expect them to be relative to when the navigation + // began. + this.lifecycleEvent({ + type: EventType.TimeToLastByte, + start: responseEnd - this.timeOrigin, + duration: 0, + }); - this.lifecycleEvent({ - type: EventType.Load, - start: loadEventStart - this.timeOrigin, - duration: 0, - }); - }); + this.lifecycleEvent({ + type: EventType.DomContentLoaded, + start: domContentLoadedEventStart - this.timeOrigin, + duration: 0, + }); + + this.lifecycleEvent({ + type: EventType.Load, + start: loadEventStart - this.timeOrigin, + duration: 0, + }); + }, + ); } else { withEntriesOfType('navigation', (entry) => { + if (entry.responseEnd > 0) { + this.lifecycleEvent({ + type: EventType.TimeToLastByte, + start: entry.responseEnd, + duration: 0, + }); + } + if (entry.domContentLoadedEventStart > 0) { this.lifecycleEvent({ type: EventType.DomContentLoaded, diff --git a/packages/performance/src/types.ts b/packages/performance/src/types.ts index ed3ee72c41..f61f778ccd 100644 --- a/packages/performance/src/types.ts +++ b/packages/performance/src/types.ts @@ -1,5 +1,6 @@ export enum EventType { TimeToFirstByte = 'ttfb', + TimeToLastByte = 'ttlb', TimeToFirstPaint = 'ttfp', TimeToFirstContentfulPaint = 'ttfcp', TimeToLargestContentfulPaint = 'ttlcp', @@ -28,6 +29,13 @@ export interface TimeToFirstByteEvent extends BasicEvent { }; } +export interface TimeToLastByteEvent extends BasicEvent { + type: EventType.TimeToLastByte; + metadata?: { + [key: string]: any; + }; +} + export interface TimeToFirstPaintEvent extends BasicEvent { type: EventType.TimeToFirstPaint; metadata?: undefined; @@ -89,6 +97,7 @@ export interface CustomEvent extends BasicEvent { export type LifecycleEvent = | TimeToFirstByteEvent + | TimeToLastByteEvent | TimeToFirstPaintEvent | TimeToFirstContentfulPaintEvent | TimeToLargestContentfulPaintEvent @@ -107,6 +116,7 @@ export type Event = export interface EventMap { [EventType.TimeToFirstByte]: TimeToFirstByteEvent; + [EventType.TimeToLastByte]: TimeToLastByteEvent; [EventType.TimeToFirstPaint]: TimeToFirstPaintEvent; [EventType.TimeToFirstContentfulPaint]: TimeToFirstContentfulPaintEvent; [EventType.TimeToLargestContentfulPaint]: TimeToLargestContentfulPaintEvent;