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

Commit 3d1a65d

Browse files
committed
Refactoring
1 parent 902465e commit 3d1a65d

3 files changed

Lines changed: 57 additions & 42 deletions

File tree

packages/opencensus-web-instrumentation-zone/src/perf-resource-timing-selector.ts

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import { Span } from '@opencensus/web-core';
1818
import { XhrPerformanceResourceTiming } from './zone-types';
19+
import { alreadyAssignedPerfEntries } from './xhr-interceptor';
1920

2021
/**
2122
* Get Browser's performance resource timing data associated to a XHR.
@@ -32,24 +33,29 @@ export function getXhrPerfomanceData(
3233
xhrUrl: string,
3334
span: Span
3435
): XhrPerformanceResourceTiming | undefined {
35-
const filteredPerfEntries = getPerfResourceEntries(xhrUrl, span);
36-
const possibleEntries = getPossiblePerfResourceEntries(filteredPerfEntries);
36+
const filteredSortedPerfEntries = getPerfSortedResourceEntries(xhrUrl, span);
37+
const possibleEntries = getPossiblePerfResourceEntries(
38+
filteredSortedPerfEntries
39+
);
3740
const bestEntry = getBestPerfResourceTiming(possibleEntries, span);
3841
return bestEntry;
3942
}
4043

4144
/**
4245
* First step for the algorithm. Filter the Performance Resource Timings by the
4346
* name (it should match the XHR URL), additionally, the start/end timings of
44-
* every performance entry should fit within the span start/end timings.
47+
* every performance entry should fit within the span start/end timings. Also,
48+
* the entry should be already assigned to a span.
4549
* These filtered performance resource entries are considered as possible
4650
* entries associated to the xhr.
4751
* Those are possible because there might be more than two entries that pass the
4852
* filter.
53+
* Additionally, the returned array is sorted by the entries' `startTime` as
54+
* getEntriesByType() already does it.
4955
* @param xhrUrl
5056
* @param span
5157
*/
52-
export function getPerfResourceEntries(
58+
export function getPerfSortedResourceEntries(
5359
xhrUrl: string,
5460
span: Span
5561
): PerformanceResourceTiming[] {
@@ -70,42 +76,36 @@ export function getPerfResourceEntries(
7076
* 'paired' with any other entry.
7177
* Thus, for this step traverse the array of resource entries and for every
7278
* entry check if it is a possible performance resource entry.
73-
* @param perfEntries
79+
* @param filteredSortedPerfEntries Sorted array of Performance Resource
80+
* Entries. This is sorted by the entries' `startTime`.
7481
*/
7582
export function getPossiblePerfResourceEntries(
76-
perfEntries: PerformanceResourceTiming[]
83+
filteredSortedPerfEntries: PerformanceResourceTiming[]
7784
): XhrPerformanceResourceTiming[] {
7885
const possiblePerfEntries = new Array<XhrPerformanceResourceTiming>();
79-
const pairedEntries = new Set<PerformanceResourceTiming>();
80-
let entryI: PerformanceResourceTiming;
81-
let entryJ: PerformanceResourceTiming;
8286
// As this part of the algorithm traverses the array twice, although,
8387
// this array is not large as the performance resource entries buffer is
8488
// cleared when there are no more running XHRs.
85-
for (let i = 0; i < perfEntries.length; i++) {
86-
entryI = perfEntries[i];
87-
// Compare every performance entry with its consecutive perfomance entries.
88-
// That way to avoid comparing twice the entries.
89-
for (let j = i + 1; j < perfEntries.length; j++) {
90-
entryJ = perfEntries[j];
91-
if (!overlappingPerfResourceTimings(entryI, entryJ)) {
89+
for (let i = 0; i < filteredSortedPerfEntries.length; i++) {
90+
const entryI = filteredSortedPerfEntries[i];
91+
// Consider the current entry as a possible entry without cors preflight
92+
// request. This is valid as this entry might be better than other possible
93+
// entries.
94+
possiblePerfEntries.push({ mainRequest: entryI });
95+
// Compare every performance entry with the perfomance entries in front of
96+
// it. This is possible as the entries are sorted by the startTime. That
97+
// way we to avoid comparing twice the entries and taking the wrong order.
98+
for (let j = i + 1; j < filteredSortedPerfEntries.length; j++) {
99+
const entryJ = filteredSortedPerfEntries[j];
100+
if (!isPossibleCorsPair(entryI, entryJ)) {
92101
// As the entries are not overlapping, that means those timings
93102
// are possible perfomance timings related to the XHR.
94103
possiblePerfEntries.push({
95104
corsPreFlightRequest: entryI,
96105
mainRequest: entryJ,
97106
});
98-
pairedEntries.add(entryI);
99-
pairedEntries.add(entryJ);
100107
}
101108
}
102-
// If the entry couldn't be paired with any other resource timing,
103-
// add it as a possible resource timing without cors preflight data.
104-
// This is possible because this entry might be better than the other
105-
// possible entries.
106-
if (!pairedEntries.has(entryI)) {
107-
possiblePerfEntries.push({ mainRequest: entryI });
108-
}
109109
}
110110
return possiblePerfEntries;
111111
}
@@ -122,20 +122,16 @@ function getBestPerfResourceTiming(
122122
let minimumGapToSpan = Number.MAX_VALUE;
123123
let bestPerfEntry: XhrPerformanceResourceTiming | undefined = undefined;
124124
for (const perfEntry of perfEntries) {
125-
let gapToSpan = Math.abs(
126-
perfEntry.mainRequest.responseEnd - span.endPerfTime
127-
);
128125
// If the current entry has cors preflight data use its `startTime` to
129126
// calculate the gap to the span.
130-
if (perfEntry.corsPreFlightRequest) {
131-
gapToSpan += Math.abs(
132-
perfEntry.corsPreFlightRequest.startTime - span.startPerfTime
133-
);
134-
} else {
135-
gapToSpan += Math.abs(
136-
perfEntry.mainRequest.startTime - span.startPerfTime
137-
);
138-
}
127+
const perfEntryStartTime = perfEntry.corsPreFlightRequest
128+
? perfEntry.corsPreFlightRequest.startTime
129+
: perfEntry.mainRequest.startTime;
130+
const gapToSpan =
131+
span.endPerfTime -
132+
perfEntry.mainRequest.responseEnd +
133+
(perfEntryStartTime - span.startPerfTime);
134+
139135
// If there is a new minimum gap to the span, update the minimum and pick
140136
// the current performance entry as the best at this point.
141137
if (gapToSpan < minimumGapToSpan) {
@@ -152,13 +148,14 @@ function isPerfEntryPartOfXhr(
152148
span: Span
153149
): boolean {
154150
return (
151+
!alreadyAssignedPerfEntries.has(entry) &&
155152
entry.name === xhrUrl &&
156153
entry.startTime >= span.startPerfTime &&
157154
entry.responseEnd <= span.endPerfTime
158155
);
159156
}
160157

161-
function overlappingPerfResourceTimings(
158+
function isPossibleCorsPair(
162159
entry1: PerformanceResourceTiming,
163160
entry2: PerformanceResourceTiming
164161
): boolean {

packages/opencensus-web-instrumentation-zone/src/xhr-interceptor.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ const xhrSpans = new Map<XhrWithUrl, Span>();
4444
// xhr tasks are being intercepted.
4545
let xhrTasksCount = 0;
4646

47+
// Set to keep track of already assigned performance resource entries to a span.
48+
// This is done as there might be some edge cases where the result might include
49+
// some already assigned entries.
50+
export const alreadyAssignedPerfEntries = new Set<PerformanceResourceTiming>();
51+
4752
/**
4853
* Intercepts task as XHR if it is a tracked task and its target object is
4954
* instance of `XMLHttpRequest`.
@@ -108,13 +113,21 @@ function endXhrSpan(xhr: XhrWithUrl): void {
108113
// This is done in order to help the browser Performance resource timings
109114
// selector algorithm to take only the data related to the current XHRs running.
110115
function maybeClearPerfResourceBuffer(): void {
111-
if (xhrTasksCount === 0) performance.clearResourceTimings();
116+
if (xhrTasksCount === 0) {
117+
performance.clearResourceTimings();
118+
// Clear the set as these entries are not longer necessary.
119+
alreadyAssignedPerfEntries.clear();
120+
}
112121
}
113122

114123
function joinPerfResourceDataToSpan(xhr: XhrWithUrl, span: Span) {
115124
const xhrPerfResourceTiming = getXhrPerfomanceData(xhr.responseURL, span);
116125
if (xhrPerfResourceTiming) {
126+
alreadyAssignedPerfEntries.add(xhrPerfResourceTiming.mainRequest);
117127
if (xhrPerfResourceTiming.corsPreFlightRequest) {
128+
alreadyAssignedPerfEntries.add(
129+
xhrPerfResourceTiming.corsPreFlightRequest
130+
);
118131
const corsPerfTiming = xhrPerfResourceTiming.corsPreFlightRequest as PerformanceResourceTimingExtended;
119132
setCorsPerfTimingAsChildSpan(corsPerfTiming, span);
120133
}

packages/opencensus-web-instrumentation-zone/test/test-perf-resource-timing-selector.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { Span } from '@opencensus/web-core';
1818
import {
19-
getPerfResourceEntries,
19+
getPerfSortedResourceEntries,
2020
getPossiblePerfResourceEntries,
2121
getXhrPerfomanceData,
2222
} from '../src/perf-resource-timing-selector';
@@ -55,7 +55,7 @@ describe('Perf Resource Timing Selector', () => {
5555
const span = createSpan(12, 30);
5656

5757
const entriesToBeFiltered = [entry2, entry3];
58-
const filteredPerfEntries = getPerfResourceEntries('/test', span);
58+
const filteredPerfEntries = getPerfSortedResourceEntries('/test', span);
5959
expect(filteredPerfEntries).toEqual(entriesToBeFiltered);
6060
});
6161
});
@@ -92,10 +92,15 @@ describe('Perf Resource Timing Selector', () => {
9292
mainRequest: entry3,
9393
} as XhrPerformanceResourceTiming;
9494

95-
expect(filteredPerfEntries.length).toBe(2);
95+
expect(filteredPerfEntries.length).toBe(5);
9696
expect(filteredPerfEntries).toContain(nonOverlappingEntry1);
9797
expect(filteredPerfEntries).toContain(nonOverlappingEntry2);
9898
expect(filteredPerfEntries).not.toContain(overlappingEntry3);
99+
// As every entry is considered a possible Xhr performance entry
100+
// without CORS preflight, check the should be present.
101+
expect(filteredPerfEntries).toContain({ mainRequest: entry1 });
102+
expect(filteredPerfEntries).toContain({ mainRequest: entry2 });
103+
expect(filteredPerfEntries).toContain({ mainRequest: entry3 });
99104
});
100105

101106
it('Should not add cors preflight value as all the entries overlap each other', () => {

0 commit comments

Comments
 (0)