Skip to content
This repository has been archived by the owner on Jun 6, 2024. It is now read-only.

Web portal: optimize jobs-table. #691

Merged
merged 3 commits into from
Jun 11, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 69 additions & 64 deletions webportal/src/app/job/job-view/job-view.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ require('datatables.net/js/jquery.dataTables.js');
require('datatables.net-bs/js/dataTables.bootstrap.js');
require('datatables.net-bs/css/dataTables.bootstrap.css');
require('datatables.net-plugins/sorting/natural.js');
require('datatables.net-plugins/sorting/title-numeric.js');
require('./job-view.component.scss');
const url = require('url');
// const moment = require('moment/moment.js');
Expand Down Expand Up @@ -155,72 +154,78 @@ const convertGpu = (gpuAttribute) => {
}
};

const loadJobs = (limit, specifiedVc) => {
loading.showLoading();
$.ajax({
url: `${webportalConfig.restServerUri}/api/v1/jobs`,
type: 'GET',
success: (data) => {
if (data.error) {
alert(data.message);
} else {
let displayDataSet = [];
let rowCount = Math.min(data.length, (limit && (/^\+?[0-9][\d]*$/.test(limit))) ? limit : 2000);
for (let i = 0; i < rowCount; i++) {
let vcName = (data[i].virtualCluster) ? data[i].virtualCluster : 'default';
if (specifiedVc && vcName !== specifiedVc) {
continue;
const loadJobs = (specifiedVc) => {
$('#view-table').html(jobTableComponent({}));

const $table = $('#job-table')
.on('preXhr.dt', loading.showLoading)
.on('xhr.dt', loading.hideLoading);

/* Uncomment following lines for simple profiling:
$table.on('preXhr.dt', function () {
console.time('request');
}).on('xhr.dt', function () {
console.timeEnd('request');
console.time('process');
}).on('preDraw.dt', function () {
console.timeEnd('process');
console.time('draw');
}).on('draw.dt', function () {
console.timeEnd('draw')
});
// */

table = $table.dataTable({
'ajax': {
url: `${webportalConfig.restServerUri}/api/v1/jobs`,
type: 'GET',
dataSrc: (data) => {
if (data.error) {
alert(data.message);
} else {
if (specifiedVc) {
return data.filter((job) => specifiedVc === (job.virtualCluster || 'default'))
} else {
return data
}
let hjss = getHumanizedJobStateString(data[i]);
let stopBtnStyle =
(hjss === 'Waiting' || hjss === 'Running') ?
'<button class="btn btn-default btn-sm" onclick="stopJob(\'' +
data[i].name + '\')">Stop</button>':
'<button class="btn btn-default btn-sm" disabled>Stop</button>';
displayDataSet.push({
jobName: '<a href="view.html?jobName=' + data[i].name + '">' + data[i].name + '</a>',
userName: data[i].username,
vcName: '<a href="virtual-clusters.html?vcName=' + vcName + '">' + vcName + '</a>',
startTime: '<span title="' + Math.round(data[i].createdTime / 1000) + '"/>' +
convertTime(false, data[i].createdTime),
duration: '<span title="' + getDurationInSeconds(data[i].createdTime, data[i].completedTime) + '"/>' +
convertTime(true, data[i].createdTime, data[i].completedTime),
retries: data[i].retries,
status: convertState(hjss),
stop: stopBtnStyle,
});
}
$('#view-table').html(jobTableComponent({}));
table = $('#job-table').dataTable({
'data': displayDataSet,
'columns': [
{title: 'Job', data: 'jobName'},
{title: 'User', data: 'userName'},
{title: 'Virtual Cluster', data: 'vcName'},
{title: 'Start Time', data: 'startTime'},
{title: 'Duration', data: 'duration'},
{title: 'Retries', data: 'retries'},
{title: 'Status', data: 'status'},
{title: 'Stop', data: 'stop'},
],
'scrollY': (($(window).height() - 265)) + 'px',
'lengthMenu': [[20, 50, 100, -1], [20, 50, 100, 'All']],
'order': [[3, 'desc']],
'columnDefs': [
{type: 'natural', targets: [0, 1, 2, 5, 6]},
{type: 'title-numeric', targets: [3, 4]},
],
'deferRender': true,
}).api();
}
loading.hideLoading();
},
error: (xhr, textStatus, error) => {
const res = JSON.parse(xhr.responseText);
alert(res.message);
loading.hideLoading();
},
});
'columns': [
{title: 'Job', data: 'name', render(name, type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this ‘render’ function play the trick of lazy calculation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, render function, especially heavy "display" branch will be calculated only when it is needed to render to the page, which runs pageSize times(as well as "autoWidth" is disabled).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked and learnt :)

if (type !== 'display') return name;
return '<a href="view.html?jobName=' + name + '">' + name + '</a>';
} },
{title: 'User', data: 'username'},
{title: 'Virtual Cluster', data: 'virtualCluster', defaultContent: 'default'},
{title: 'Start Time', data: 'createdTime', render(createdTime, type) {
if (type !== 'display') return Math.round(createdTime / 1000);
return convertTime(false, createdTime)
}},
{title: 'Duration', data: null, render({createdTime, completedTime}, type) {
if (type !== 'display') return getDurationInSeconds(createdTime, completedTime);
return convertTime(true, createdTime, completedTime)
} },
{title: 'Retries', data: 'retries'},
{title: 'Status', data: null, render: getHumanizedJobStateString },
{title: 'Stop', data: null, render(job, type) {
let hjss = getHumanizedJobStateString(job);
return (hjss === 'Waiting' || hjss === 'Running') ?
'<button class="btn btn-default btn-sm" onclick="stopJob(\'' +
job.name + '\')">Stop</button>':
'<button class="btn btn-default btn-sm" disabled>Stop</button>';
} },
],
'scrollY': (($(window).height() - 265)) + 'px',
'lengthMenu': [[20, 50, 100, -1], [20, 50, 100, 'All']],
'order': [[3, 'desc']],
'columnDefs': [
{type: 'natural', targets: [0, 1, 2, 5, 6]},
{type: 'num', targets: [3, 4]},
],
'deferRender': true,
'autoWidth': false,
Copy link
Contributor

@hwuu hwuu Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does autoWidth mean and what will happen if it is disabled?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When autoWidth is enabled, DataTables will layout all possible rows (which means 350,000 rows in the benchmarks). collect widths of every cell and calculate the most suitable width of each column. When it is disabled, widths of each column will be decided by the browser, based on data in the current page. So this will cause different page have different column widths (but total will remain 100% of table width, which is set explicitly in CSS)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Sounds good.

}).api();
};

const stopJob = (jobName) => {
Expand Down Expand Up @@ -372,7 +377,7 @@ $(document).ready(() => {
loadJobDetail(query['jobName']);
$('#content-wrapper').css({'overflow': 'auto'});
} else {
loadJobs(query['limit'], query['vcName']);
loadJobs(query['vcName']);
$('#content-wrapper').css({'overflow': 'hidden'});
}
});
Expand Down