Skip to content

Commit

Permalink
fix: Use monotonic clock to compute durations (#2441)
Browse files Browse the repository at this point in the history
A monotonic clock is monotonically increasing and not subject to system
clock adjustments or system clock skew. The difference between any two
chronologically recorded time values returned from the Performance.now()
method MUST never be negative if the two time values have the same time
origin.

The same guarantee above does not exist for the difference between two
calls to `new Date().getTime()` as used by `timestampWithMs()`.

Resources:

https://stackoverflow.com/questions/7272395/monotonically-increasing-time-in-javascript
https://caniuse.com/#search=performance.now
https://www.w3.org/TR/hr-time/#sec-monotonic-clock

Co-authored-by: Kamil Ogórek <[email protected]>
  • Loading branch information
rhcarvalho and kamilogorek authored Feb 27, 2020
1 parent 2e11882 commit 13d8398
Showing 1 changed file with 33 additions and 2 deletions.
35 changes: 33 additions & 2 deletions packages/apm/src/span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,26 @@

import { getCurrentHub, Hub } from '@sentry/hub';
import { Span as SpanInterface, SpanContext, SpanStatus } from '@sentry/types';
import { dropUndefinedKeys, isInstanceOf, logger, timestampWithMs, uuid4 } from '@sentry/utils';
import {
dropUndefinedKeys,
dynamicRequire,
getGlobalObject,
isInstanceOf,
isNodeEnv,
logger,
timestampWithMs,
uuid4,
} from '@sentry/utils';

const global = getGlobalObject<Window>();

const performanceNow = (() => {
if (isNodeEnv()) {
const { performance } = dynamicRequire(module, 'perf_hooks');
return performance.now;
}
return global.performance.now.bind(global.performance);
})();

// TODO: Should this be exported?
export const TRACEPARENT_REGEXP = new RegExp(
Expand Down Expand Up @@ -81,6 +100,17 @@ export class Span implements SpanInterface, SpanContext {
*/
public readonly startTimestamp: number = timestampWithMs();

/**
* Internal start time tracked with a monotonic clock.
*
* Works with mostly any browser version released since 2012.
* https://caniuse.com/#search=performance.now
*
* Works with Node.js v8.5.0 or higher.
* https://nodejs.org/api/perf_hooks.html#perf_hooks_performance_now
*/
private readonly _startTimestampMonotonic: number = performanceNow();

/**
* Finish timestamp of the span.
*/
Expand Down Expand Up @@ -261,7 +291,8 @@ export class Span implements SpanInterface, SpanContext {
return undefined;
}

this.timestamp = timestampWithMs();
const durationSeconds = (performanceNow() - this._startTimestampMonotonic) / 1000;
this.timestamp = this.startTimestamp + durationSeconds;

if (this.spanRecorder === undefined) {
return undefined;
Expand Down

0 comments on commit 13d8398

Please sign in to comment.