Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 29aaad1

Browse files
Add function to adjust performance origin based on server time (#14)
1 parent 62ab43d commit 29aaad1

2 files changed

Lines changed: 113 additions & 2 deletions

File tree

packages/opencensus-web-core/src/common/time-util.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,62 @@
1414
* limitations under the License.
1515
*/
1616

17+
/** Polyfill value of `performance.timeOrigin` for browser that lack support. */
1718
let perfOriginPolyfill = 0;
19+
/** If non-zero, the approximate peformance clock origin in server time. */
20+
let perfOriginInServerTime = 0;
1821

19-
/** Returns the origin of the browser performance clock in epoch millis. */
22+
/**
23+
* Returns the origin of the browser performance clock in epoch millis.
24+
* This can be adjusted to align more closely with the server's clock using the
25+
* `adjustPerfTimeOrigin` function.
26+
*/
2027
export function getPerfTimeOrigin(): number {
28+
return perfOriginInServerTime ? perfOriginInServerTime :
29+
getClientPerfTimeOrigin();
30+
}
31+
32+
/**
33+
* Adjusts the performance clock time origin based on server clock times for the
34+
* start and duration of the navigation fetch (the initial HTML load request).
35+
*
36+
* This adjusts the client clock such that the network time is evenly spread on
37+
* both sides of the request, so that the server's span will be positioned right
38+
* in the middle of the client's span. This enables visualizing the server and
39+
* client spans no the same timeline even if they have clock skew.
40+
*
41+
* @param serverNavFetchStartTime The server's measurement of the request start
42+
* in epoch milliseconds from the server clock. This would be sent back to
43+
* the client in a <script> in the rendered HTML.
44+
* @param serverNavFetchDuration The server's measurement of the request
45+
* duration in milliseconds. This would also be sent to the client.
46+
*/
47+
export function adjustPerfTimeOrigin(
48+
serverNavFetchStartTime: number, serverNavFetchDuration: number) {
49+
// If there is no performance timing info, we don't have the client's
50+
// start/end times for the navigation fetch, so we can't accurately use the
51+
// server times to adjust the client time origin.
52+
if (!performance.timing) return;
53+
const clientStart = performance.timing.requestStart;
54+
const clientEnd = performance.timing.responseStart;
55+
const clientNavFetchDuration = clientEnd - clientStart;
56+
57+
// Server time is more than client time, which we don't expect, so don't try
58+
// to adjust the time origin.
59+
if (serverNavFetchDuration > clientNavFetchDuration) return;
60+
61+
const networkTime = clientNavFetchDuration - serverNavFetchDuration;
62+
const halfNetworkTime = networkTime / 2;
63+
const clientStartInServerTime = serverNavFetchStartTime - halfNetworkTime;
64+
perfOriginInServerTime = clientStartInServerTime - clientStart;
65+
}
66+
67+
/** Helper function used for testing. */
68+
function clearAdjustedPerfTime() {
69+
perfOriginInServerTime = 0;
70+
}
71+
72+
function getClientPerfTimeOrigin() {
2173
if (performance.timeOrigin) return performance.timeOrigin;
2274
if (!perfOriginPolyfill) {
2375
perfOriginPolyfill = Date.now() - performance.now();
@@ -59,3 +111,5 @@ function wholeAndFraction(num: number): [number, number] {
59111
const fraction = num - whole;
60112
return [whole, fraction];
61113
}
114+
115+
export const TEST_ONLY = {clearAdjustedPerfTime};

packages/opencensus-web-core/test/test-time-util.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {getDateForPerfTime, getIsoDateStrForPerfTime, getPerfTimeOrigin} from '../src/common/time-util';
17+
import {adjustPerfTimeOrigin, getDateForPerfTime, getIsoDateStrForPerfTime, getPerfTimeOrigin, TEST_ONLY} from '../src/common/time-util';
1818

1919
describe('getPerfTimeOrigin', () => {
2020
it('returns `performance.timeOrigin` if set', () => {
@@ -32,6 +32,63 @@ describe('getPerfTimeOrigin', () => {
3232
});
3333
});
3434

35+
describe('adjustPerfTimeOrigin', () => {
36+
const CLIENT_TIME_ORIGIN = 1548000000000;
37+
beforeEach(() => {
38+
spyOnProperty(performance, 'timeOrigin')
39+
.and.returnValue(CLIENT_TIME_ORIGIN);
40+
});
41+
afterEach(() => {
42+
TEST_ONLY.clearAdjustedPerfTime();
43+
});
44+
45+
it('keeps client time origin if no performance timing missing', () => {
46+
spyOnProperty(performance, 'timing').and.returnValue(undefined);
47+
adjustPerfTimeOrigin(1548000001000.2, 5.1);
48+
expect(getPerfTimeOrigin()).toBe(CLIENT_TIME_ORIGIN);
49+
});
50+
51+
it('keeps client time origin if server time longer than client', () => {
52+
// Client nav fetch duration is 5ms
53+
spyOnProperty(performance.timing, 'requestStart').and.returnValue(10.1);
54+
spyOnProperty(performance.timing, 'responseStart').and.returnValue(15.1);
55+
56+
// Server nav fetch duration is 10ms
57+
adjustPerfTimeOrigin(1548000001000.2, /* serverNavFetchDuration */ 10);
58+
59+
expect(getPerfTimeOrigin()).toBe(CLIENT_TIME_ORIGIN);
60+
});
61+
62+
it('adjusts origin to center server span in client span', () => {
63+
const clientNavFetchStartInPerfTime = 10; // Performance clock millis.
64+
spyOnProperty(performance.timing, 'requestStart')
65+
.and.returnValue(clientNavFetchStartInPerfTime);
66+
const clientNavFetchEndInPerfTime = 18; // Performance clock millis.
67+
spyOnProperty(performance.timing, 'responseStart')
68+
.and.returnValue(clientNavFetchEndInPerfTime);
69+
70+
const serverNavFetchStartEpochMillis = 1500000001000; // Epoch millis.
71+
const serverNavFetchDuration = 6; // Duration millis
72+
adjustPerfTimeOrigin(
73+
serverNavFetchStartEpochMillis, serverNavFetchDuration);
74+
75+
// Calculations to make the expectation clearer:
76+
const clientNavFetchDuration =
77+
clientNavFetchEndInPerfTime - clientNavFetchStartInPerfTime;
78+
expect(clientNavFetchDuration).toBe(8); // Duration millis
79+
const networkTime = clientNavFetchDuration - serverNavFetchDuration;
80+
expect(networkTime).toBe(2); // Duration millis
81+
const clientNavStartInEpochMillis =
82+
serverNavFetchStartEpochMillis - networkTime / 2;
83+
expect(clientNavStartInEpochMillis).toBe(1500000000999);
84+
const perfOriginInEpochMillis =
85+
clientNavStartInEpochMillis - clientNavFetchStartInPerfTime;
86+
expect(perfOriginInEpochMillis).toBe(1500000000989);
87+
88+
expect(getPerfTimeOrigin()).toBe(perfOriginInEpochMillis);
89+
});
90+
});
91+
3592
describe('getDateForPerfTime', () => {
3693
it('calculates date for perf time based on time origin', () => {
3794
spyOnProperty(performance, 'timeOrigin').and.returnValue(1548000000000);

0 commit comments

Comments
 (0)