Skip to content

Commit 196ef08

Browse files
authored
Bug 1967984 - show the Failure Summary faster when changing the selected job by doing more network requests in parallel, r=Aryx. (#8876)
1 parent f29f86c commit 196ef08

File tree

6 files changed

+135
-108
lines changed

6 files changed

+135
-108
lines changed

tests/ui/job-view/App_test.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,27 @@ describe('App', () => {
8282
...fullJob,
8383
task_id: 'secondTaskId',
8484
});
85+
fetchMock.get(
86+
getProjectUrl('/jobs/259537372/bug_suggestions/', repoName),
87+
[],
88+
);
8589

8690
fetchMock.get(getProjectUrl('/jobs/259539665/', repoName), {
8791
...fullJob,
8892
task_id: 'MirsMc8UQPeSBC3yKMSlPw',
8993
});
94+
fetchMock.get(
95+
getProjectUrl('/jobs/259539665/bug_suggestions/', repoName),
96+
[],
97+
);
9098
fetchMock.get(getProjectUrl('/jobs/259539664/', repoName), {
9199
...fullJob,
92100
task_id: 'Fe4GqwoZQSStNUbe4EeSPQ',
93101
});
102+
fetchMock.get(
103+
getProjectUrl('/jobs/259539664/bug_suggestions/', repoName),
104+
[],
105+
);
94106
fetchMock.get(
95107
`begin:${getProjectUrl('/performance/data/?job_id=', repoName)}`,
96108
[],

tests/ui/shared/FailureSummaryTab_test.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ describe('FailureSummaryTab', () => {
5858
/>
5959
<FailureSummaryTab
6060
selectedJob={selectedJob}
61+
selectedJobId={selectedJob.id}
6162
jobLogUrls={jobLogUrls}
6263
logParseStatus="parsed"
6364
reftestUrl="boo"

ui/job-view/details/DetailsPanel.jsx

Lines changed: 105 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ class DetailsPanel extends React.Component {
115115

116116
this.setState(
117117
{
118-
jobDetails: [],
119118
suggestions: [],
120119
jobDetailLoading: true,
121120
jobArtifactsLoading: true,
@@ -167,26 +166,96 @@ class DetailsPanel extends React.Component {
167166
this.selectJobController.signal,
168167
);
169168

170-
const phSeriesPromise = PerfSeriesModel.getSeriesData(
169+
const performancePromise = PerfSeriesModel.getSeriesData(
171170
currentRepo.name,
172171
{
173172
job_id: selectedJob.id,
174173
},
175-
);
174+
).then(async (phSeriesResult) => {
175+
const performanceData = Object.values(phSeriesResult).reduce(
176+
(a, b) => [...a, ...b],
177+
[],
178+
);
179+
let perfJobDetail = [];
180+
181+
if (performanceData.length) {
182+
const signatureIds = [
183+
...new Set(performanceData.map((perf) => perf.signature_id)),
184+
];
185+
const seriesListList = await Promise.all(
186+
chunk(signatureIds, 20).map((signatureIdChunk) =>
187+
PerfSeriesModel.getSeriesList(currentRepo.name, {
188+
id: signatureIdChunk,
189+
}),
190+
),
191+
);
192+
const mappedFrameworks = {};
193+
frameworks.forEach((element) => {
194+
mappedFrameworks[element.id] = element.name;
195+
});
196+
197+
const seriesList = seriesListList
198+
.map((item) => item.data)
199+
.reduce((a, b) => [...a, ...b], []);
200+
201+
perfJobDetail = performanceData
202+
.map((d) => ({
203+
series: seriesList.find((s) => d.signature_id === s.id),
204+
...d,
205+
}))
206+
.map((d) => ({
207+
url: `/perfherder/graphs?series=${[
208+
currentRepo.name,
209+
d.signature_id,
210+
1,
211+
d.series.frameworkId,
212+
]}&selected=${[d.signature_id, d.id]}`,
213+
shouldAlert: d.series.should_alert,
214+
value: d.value,
215+
measurementUnit: d.series.measurementUnit,
216+
lowerIsBetter: d.series.lowerIsBetter,
217+
title: d.series.name,
218+
suite: d.series.suite,
219+
options: d.series.options.join(' '),
220+
frameworkName: mappedFrameworks[d.series.frameworkId],
221+
perfdocs: new Perfdocs(
222+
mappedFrameworks[d.series.frameworkId],
223+
d.series.suite,
224+
d.series.platform,
225+
d.series.name,
226+
),
227+
}));
228+
}
229+
perfJobDetail.sort((a, b) => {
230+
// Sort perfJobDetails by value of shouldAlert in a particular order:
231+
// first true values, after that null values and then false.
232+
if (a.shouldAlert === true) {
233+
return -1;
234+
}
235+
if (a.shouldAlert === false) {
236+
return 1;
237+
}
238+
if (a.shouldAlert === null && b.shouldAlert === true) {
239+
return 1;
240+
}
241+
if (a.shouldAlert === null && b.shouldAlert === false) {
242+
return -1;
243+
}
244+
return 0;
245+
});
246+
this.setState({
247+
perfJobDetail,
248+
});
249+
});
176250

177251
Promise.all([
178252
jobPromise,
179253
jobLogUrlPromise,
180-
phSeriesPromise,
181254
builtFromArtifactPromise,
255+
this.updateClassifications(),
182256
])
183257
.then(
184-
async ([
185-
jobResult,
186-
jobLogUrlResult,
187-
phSeriesResult,
188-
builtFromArtifactResult,
189-
]) => {
258+
async ([jobResult, jobLogUrlResult, builtFromArtifactResult]) => {
190259
// This version of the job has more information than what we get in the main job list. This
191260
// is what we'll pass to the rest of the details panel.
192261
// Don't update the job instance in the greater job field so as to not add the memory overhead
@@ -242,93 +311,31 @@ class DetailsPanel extends React.Component {
242311
currentRepo.name,
243312
);
244313
const logViewerFullUrl = `${window.location.origin}${logViewerUrl}`;
245-
const performanceData = Object.values(phSeriesResult).reduce(
246-
(a, b) => [...a, ...b],
247-
[],
248-
);
249-
let perfJobDetail = [];
250-
251-
if (performanceData.length) {
252-
const signatureIds = [
253-
...new Set(performanceData.map((perf) => perf.signature_id)),
254-
];
255-
const seriesListList = await Promise.all(
256-
chunk(signatureIds, 20).map((signatureIdChunk) =>
257-
PerfSeriesModel.getSeriesList(currentRepo.name, {
258-
id: signatureIdChunk,
259-
}),
260-
),
261-
);
262-
const mappedFrameworks = {};
263-
frameworks.forEach((element) => {
264-
mappedFrameworks[element.id] = element.name;
265-
});
266-
267-
const seriesList = seriesListList
268-
.map((item) => item.data)
269-
.reduce((a, b) => [...a, ...b], []);
270-
271-
perfJobDetail = performanceData
272-
.map((d) => ({
273-
series: seriesList.find((s) => d.signature_id === s.id),
274-
...d,
275-
}))
276-
.map((d) => ({
277-
url: `/perfherder/graphs?series=${[
278-
currentRepo.name,
279-
d.signature_id,
280-
1,
281-
d.series.frameworkId,
282-
]}&selected=${[d.signature_id, d.id]}`,
283-
shouldAlert: d.series.should_alert,
284-
value: d.value,
285-
measurementUnit: d.series.measurementUnit,
286-
lowerIsBetter: d.series.lowerIsBetter,
287-
title: d.series.name,
288-
suite: d.series.suite,
289-
options: d.series.options.join(' '),
290-
frameworkName: mappedFrameworks[d.series.frameworkId],
291-
perfdocs: new Perfdocs(
292-
mappedFrameworks[d.series.frameworkId],
293-
d.series.suite,
294-
d.series.platform,
295-
d.series.name,
296-
),
297-
}));
314+
315+
const newState = {
316+
selectedJobFull,
317+
jobLogUrls,
318+
logParseStatus,
319+
logViewerUrl,
320+
logViewerFullUrl,
321+
jobRevision,
322+
};
323+
324+
// Only wait for the performance data before setting
325+
// jobDetailLoading to false if we will not be showing
326+
// the Failure Summary panel by default.
327+
if (
328+
!['busted', 'testfailed', 'exception'].includes(
329+
selectedJobFull.resultStatus,
330+
)
331+
) {
332+
this.setState(newState);
333+
await performancePromise;
334+
this.setState({ jobDetailLoading: false });
335+
} else {
336+
newState.jobDetailLoading = false;
337+
this.setState(newState);
298338
}
299-
perfJobDetail.sort((a, b) => {
300-
// Sort perfJobDetails by value of shouldAlert in a particular order:
301-
// first true values, after that null values and then false.
302-
if (a.shouldAlert === true) {
303-
return -1;
304-
}
305-
if (a.shouldAlert === false) {
306-
return 1;
307-
}
308-
if (a.shouldAlert === null && b.shouldAlert === true) {
309-
return 1;
310-
}
311-
if (a.shouldAlert === null && b.shouldAlert === false) {
312-
return -1;
313-
}
314-
return 0;
315-
});
316-
317-
this.setState(
318-
{
319-
selectedJobFull,
320-
jobLogUrls,
321-
logParseStatus,
322-
logViewerUrl,
323-
logViewerFullUrl,
324-
perfJobDetail,
325-
jobRevision,
326-
},
327-
async () => {
328-
await this.updateClassifications();
329-
this.setState({ jobDetailLoading: false });
330-
},
331-
);
332339
},
333340
)
334341
.finally(() => {
@@ -346,6 +353,7 @@ class DetailsPanel extends React.Component {
346353
classificationMap,
347354
classificationTypes,
348355
isPinBoardVisible,
356+
selectedJob,
349357
} = this.props;
350358
const {
351359
selectedJobFull,
@@ -403,6 +411,7 @@ class DetailsPanel extends React.Component {
403411
/>
404412
<span className="job-tabs-divider" />
405413
<TabsPanel
414+
selectedJob={selectedJob}
406415
selectedJobFull={selectedJobFull}
407416
currentRepo={currentRepo}
408417
jobDetails={jobDetails}

ui/job-view/details/tabs/TabsPanel.jsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,26 @@ class TabsPanel extends React.Component {
4141
}
4242

4343
static getDerivedStateFromProps(props, state) {
44-
const { perfJobDetail, selectedJobFull } = props;
44+
const { perfJobDetail, selectedJob } = props;
4545

4646
// This fires every time the props change. But we only want to figure out the new default
4747
// tab when we get a new job. However, the job could change, then later, the perf details fetch
4848
// returns. So we need to check for a change in the size of the perfJobDetail too.
4949
if (
50-
state.jobId !== selectedJobFull.id ||
51-
state.perfJobDetailSize !== perfJobDetail.length
50+
!!selectedJob &&
51+
(state.jobId !== selectedJob.id ||
52+
state.perfJobDetailSize !== perfJobDetail.length)
5253
) {
5354
const tabIndex = TabsPanel.getDefaultTabIndex(
54-
selectedJobFull.resultStatus,
55+
selectedJob.resultStatus,
5556
props,
5657
);
57-
5858
return {
5959
tabIndex,
6060
// Every time we select a different job we need to let the component
6161
// let us know if we should enable the tab
6262
enableTestGroupsTab: false,
63-
jobId: selectedJobFull.id,
63+
jobId: selectedJob.id,
6464
perfJobDetailSize: perfJobDetail.length,
6565
};
6666
}
@@ -135,6 +135,7 @@ class TabsPanel extends React.Component {
135135
classificationMap,
136136
logViewerFullUrl,
137137
clearSelectedJob,
138+
selectedJob,
138139
selectedJobFull,
139140
currentRepo,
140141
pinJob,
@@ -224,6 +225,7 @@ class TabsPanel extends React.Component {
224225
<TabPanel>
225226
<FailureSummaryTab
226227
selectedJob={selectedJobFull}
228+
selectedJobId={selectedJob && selectedJob.id}
227229
jobLogUrls={jobLogUrls}
228230
jobDetails={jobDetails}
229231
logParseStatus={logParseStatus}
@@ -295,6 +297,7 @@ TabsPanel.propTypes = {
295297
pinnedJobs: PropTypes.shape({}).isRequired,
296298
bugs: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
297299
clearSelectedJob: PropTypes.func.isRequired,
300+
selectedJob: PropTypes.shape({}).isRequired,
298301
selectedJobFull: PropTypes.shape({}).isRequired,
299302
currentRepo: PropTypes.shape({}).isRequired,
300303
perfJobDetail: PropTypes.arrayOf(PropTypes.shape({})),

ui/push-health/details/DetailsPanel.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ class DetailsPanel extends React.Component {
193193
<TabPanel>
194194
<FailureSummaryTab
195195
selectedJob={selectedTaskFull}
196+
selectedJobId={selectedTaskFull.id}
196197
jobLogUrls={selectedTaskFull.logs}
197198
logParseStatus="unknown"
198199
logViewerFullUrl={getLogViewerUrl(

ui/shared/tabs/failureSummary/FailureSummaryTab.jsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ class FailureSummaryTab extends React.Component {
4141
}
4242

4343
componentDidUpdate(prevProps) {
44-
const { selectedJob } = this.props;
44+
const { selectedJobId } = this.props;
4545

4646
if (
47-
!!selectedJob &&
47+
!!selectedJobId &&
4848
!!prevProps.selectedJob &&
49-
selectedJob.id !== prevProps.selectedJob.id
49+
selectedJobId !== prevProps.selectedJobId
5050
) {
5151
this.loadBugSuggestions();
5252
}
@@ -129,13 +129,13 @@ class FailureSummaryTab extends React.Component {
129129
};
130130

131131
loadBugSuggestions = () => {
132-
const { selectedJob } = this.props;
132+
const { selectedJobId } = this.props;
133133

134-
if (!selectedJob) {
134+
if (!selectedJobId) {
135135
return;
136136
}
137137
this.setState({ bugSuggestionsLoading: true });
138-
BugSuggestionsModel.get(selectedJob.id).then(async (suggestions) => {
138+
BugSuggestionsModel.get(selectedJobId).then(async (suggestions) => {
139139
suggestions.forEach((suggestion) => {
140140
suggestion.bugs.too_many_open_recent =
141141
suggestion.bugs.open_recent.length > thBugSuggestionLimit;
@@ -351,6 +351,7 @@ class FailureSummaryTab extends React.Component {
351351

352352
FailureSummaryTab.propTypes = {
353353
selectedJob: PropTypes.shape({}).isRequired,
354+
selectedJobId: PropTypes.number.isRequired,
354355
jobLogUrls: PropTypes.arrayOf({
355356
id: PropTypes.number.isRequired,
356357
job_id: PropTypes.number.isRequired,

0 commit comments

Comments
 (0)