Skip to content

Commit dece89a

Browse files
wmakrhcarvalhodashed
authored
fix: Avoid performance.timeOrigin if too skewed (#3356)
Try to use the best of performance.timeOrigin or performance.timing.navigationStart as long as they are near the current time reported by Date.now. The intention is to mitigate transactions that are reported with a huge time skew, particularly from Firefox. This is trying to address the inconsistencies between browsers by always trying to use timeOrigin first, as long as its consistent with the current time, otherwise, use the navigationStart if its consistent with the current time, eventually fallback to date if all else fails. We also remember what source is used for the time origin, so we can tag events and analyze later. Co-authored-by: Rodolfo Carvalho <[email protected]> Co-authored-by: Alberto Leal <[email protected]>
1 parent 28c540c commit dece89a

File tree

1 file changed

+30
-2
lines changed

1 file changed

+30
-2
lines changed

packages/utils/src/time.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,23 +126,51 @@ export const timestampWithMs = timestampInSeconds;
126126
*/
127127
export const usingPerformanceAPI = platformPerformance !== undefined;
128128

129+
/**
130+
* Internal helper to store what is the source of browserPerformanceTimeOrigin below. For debugging only.
131+
*/
132+
export let _browserPerformanceTimeOriginMode: string;
133+
129134
/**
130135
* The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
131136
* performance API is available.
132137
*/
133138
export const browserPerformanceTimeOrigin = ((): number | undefined => {
139+
// Unfortunately browsers may report an inaccurate time origin data, through either performance.timeOrigin or
140+
// performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
141+
// data as reliable if they are within a reasonable threshold of the current time.
142+
134143
const { performance } = getGlobalObject<Window>();
135144
if (!performance) {
145+
_browserPerformanceTimeOriginMode = 'none';
136146
return undefined;
137147
}
138-
if (performance.timeOrigin) {
148+
149+
const threshold = 3600 * 1000;
150+
151+
const timeOriginIsReliable =
152+
performance.timeOrigin && Math.abs(performance.timeOrigin + performance.now() - Date.now()) < threshold;
153+
if (timeOriginIsReliable) {
154+
_browserPerformanceTimeOriginMode = 'timeOrigin';
139155
return performance.timeOrigin;
140156
}
157+
141158
// While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
142159
// is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
143160
// Also as of writing, performance.timing is not available in Web Workers in mainstream browsers, so it is not always
144161
// a valid fallback. In the absence of an initial time provided by the browser, fallback to the current time from the
145162
// Date API.
146163
// eslint-disable-next-line deprecation/deprecation
147-
return (performance.timing && performance.timing.navigationStart) || Date.now();
164+
const navigationStart = performance.timing && performance.timing.navigationStart;
165+
const hasNavigationStart = typeof navigationStart === 'number';
166+
const navigationStartIsReliable =
167+
hasNavigationStart && Math.abs(navigationStart + performance.now() - Date.now()) < threshold;
168+
if (navigationStartIsReliable) {
169+
_browserPerformanceTimeOriginMode = 'navigationStart';
170+
return navigationStart;
171+
}
172+
173+
// Either both timeOrigin and navigationStart are skewed or neither is available, fallback to Date.
174+
_browserPerformanceTimeOriginMode = 'dateNow';
175+
return Date.now();
148176
})();

0 commit comments

Comments
 (0)