blob: 85168e30d5244f4b631fa89e0bd1a6db26b515be [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
/**
* `register` without params will load the module which using require
*/
register(function (jpmApp) {
/**
* @param {{}} $scope
* @param {{}} $scope.trendChart
*/
jpmApp.controller("compareCtrl", function ($q, $wrapState, $scope, PageConfig, Time, Entity, JPM) {
$scope.site = $wrapState.param.siteId;
$scope.jobDefId = $wrapState.param.jobDefId;
$scope.jobTrendCategory = [];
$scope.fromJob = null;
$scope.toJob = null;
PageConfig.title = "Job History";
PageConfig.subTitle = $scope.jobDefId;
$scope.getStateClass = JPM.getStateClass;
var browserAction = true;
// ==================================================================
// = Fetch Job List =
// ==================================================================
var endTime = new Time();
var startTime = endTime.clone().subtract(30, 'day');
var condition = JPM.condition({site: $scope.site, jobDefId:$scope.jobDefId});
var jobList = $scope.jobList = JPM.list("JobExecutionService", condition, startTime, endTime, [], 10000);
jobList._promise.then(function () {
if(jobList.length <= 1) {
$.dialog({
title: "No statistic info",
content: "Current job do not have enough statistic info. Please check 'jobDefId' if your job have run many times."
});
}
function findJob(jobId) {
return common.array.find(jobId, jobList, "tags.jobId");
}
var TASK_BUCKET_TIMES = [0, 30, 60, 120, 300, 600, 1800, 3600, 7200, 18000];
function taskDistribution(jobId) {
return JPM.taskDistribution($scope.site, jobId, TASK_BUCKET_TIMES.join(",")).then(
/**
* @param {{}} res
* @param {{}} res.data
* @param {[]} res.data.finishedTaskCount
* @param {[]} res.data.runningTaskCount
*/
function (res) {
var result = {};
var data = res.data;
var finishedTaskCount = data.finishedTaskCount;
var runningTaskCount = data.runningTaskCount;
/**
* @param {number} item.taskCount
*/
var finishedTaskData = $.map(finishedTaskCount, function (item) {
return item.taskCount;
});
/**
* @param {number} item.taskCount
*/
var runningTaskData = $.map(runningTaskCount, function (item) {
return item.taskCount;
});
result.taskSeries = [{
name: "Finished Tasks",
type: "bar",
stack: jobId,
data: finishedTaskData
}, {
name: "Running Tasks",
type: "bar",
stack: jobId,
data: runningTaskData
}];
result.finishedTaskCount = finishedTaskCount;
result.runningTaskCount = runningTaskCount;
return result;
});
}
var taskDistributionCategory = $.map(TASK_BUCKET_TIMES, function (current, i) {
var curDes = Time.diffStr(TASK_BUCKET_TIMES[i] * 1000);
var nextDes = Time.diffStr(TASK_BUCKET_TIMES[i + 1] * 1000);
if(!curDes && nextDes) {
return "<" + nextDes;
} else if(nextDes) {
return curDes + "\n~\n" + nextDes;
}
return ">" + curDes;
});
// ========================= Job Trend ==========================
function refreshParam() {
browserAction = false;
$wrapState.go(".", {
from: common.getValueByPath($scope.fromJob, "tags.jobId"),
to: common.getValueByPath($scope.toJob, "tags.jobId")
});
setTimeout(function () {
browserAction = true;
}, 0);
}
function getMarkPoint(name, x, y, color) {
return {
name: name,
silent: true,
coord: [x, y],
symbolSize: 20,
label: {
normal: { show: false },
emphasis: { show: false }
}, itemStyle: {
normal: { color: color }
}
};
}
function refreshTrendMarkPoint() {
var fromX = null, fromY = null;
var toX = null, toY = null;
$.each(jobList, function (index, job) {
if($scope.fromJob && $scope.fromJob.tags.jobId === job.tags.jobId) {
fromX = index;
fromY = job.durationTime;
}
if($scope.toJob && $scope.toJob.tags.jobId === job.tags.jobId) {
toX = index;
toY = job.durationTime;
}
});
markPoint.data = [];
if(!common.isEmpty(fromX)) {
markPoint.data.push(getMarkPoint("<From Job>", fromX, fromY, "#00c0ef"));
}
if(!common.isEmpty(toX)) {
markPoint.data.push(getMarkPoint("<To Job>", toX, toY, "#3c8dbc"));
}
$scope.trendChart.refresh();
}
var jobListTrend = $.map(jobList, function (job) {
var time = Time.format(job.startTime);
$scope.jobTrendCategory.push(time);
return job.durationTime;
});
$scope.jobTrendOption = {
yAxis: [{
axisLabel: {
formatter: function (value) {
return Time.diffStr(value);
}
}
}],
tooltip: {
formatter: function (points) {
var point = points[0];
return point.name + "<br/>" +
'<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
point.seriesName + ": " + Time.diffStr(point.value);
}
}
};
var markPoint = {
data: [],
silent: true
};
$scope.jobTrendSeries = [{
name: "Job Duration",
type: "line",
data: jobListTrend,
symbolSize: 10,
showSymbol: false,
markPoint: markPoint
}];
$scope.compareJobSelect = function (e, job) {
var index = e.dataIndex;
job = job || jobList[index];
if(!job) return;
var event = e.event ? e.event.event : e;
if(event.ctrlKey) {
$scope.fromJob = job;
} else if(event.shiftKey) {
$scope.toJob = job;
} else {
if ($scope.fromJob) {
$scope.toJob = $scope.fromJob;
}
$scope.fromJob = job;
}
refreshTrendMarkPoint();
refreshParam();
refreshComparisonDashboard();
};
$scope.exchangeJobs = function () {
var tmpJob = $scope.fromJob;
$scope.fromJob = $scope.toJob;
$scope.toJob = tmpJob;
refreshTrendMarkPoint();
refreshParam();
refreshComparisonDashboard();
};
// ======================= Job Comparison =======================
$scope.taskOption = {
xAxis: {axisTick: { show: true }}
};
function getComparedValue(path) {
var val1 = common.getValueByPath($scope.fromJob, path);
var val2 = common.getValueByPath($scope.toJob, path);
return common.number.compare(val1, val2);
}
$scope.jobCompareClass = function (path) {
var diff = getComparedValue(path);
if(typeof diff !== "number" || Math.abs(diff) < 0.01) return "hide";
if(diff < 0.05) return "label label-success";
if(diff < 0.15) return "label label-warning";
return "label label-danger";
};
$scope.jobCompareValue = function (path) {
var diff = getComparedValue(path);
return (diff >= 0 ? "+" : "") + Math.floor(diff * 100) + "%";
};
/**
* get 2 interval data list category. (minutes level)
* @param {[]} list1
* @param {[]} list2
* @return {Array}
*/
function intervalCategories(list1, list2) {
var len = Math.max(list1.length, list2.length);
var category = [];
for(var i = 0 ; i < len ; i += 1) {
category.push((i + 1) + "min");
}
return category;
}
function refreshComparisonDashboard() {
if(!$scope.fromJob || !$scope.toJob) return;
var fromJobCond = {
jobId: $scope.fromJob.tags.jobId,
site: $scope.site
};
var toJobCond = {
jobId: $scope.toJob.tags.jobId,
site: $scope.site
};
var from_startTime = Time($scope.fromJob.startTime).subtract(1, "hour");
var from_endTime = ($scope.fromJob.endTime ? Time($scope.fromJob.endTime) : Time()).add(1, "hour");
var to_startTime = Time($scope.toJob.startTime).subtract(1, "hour");
var to_endTime = ($scope.toJob.endTime ? Time($scope.toJob.endTime) : Time()).add(1, "hour");
$scope.fromJob._cache = $scope.fromJob._cache || {};
$scope.toJob._cache = $scope.toJob._cache || {};
/**
* Generate metric level chart series
* @param {string} metric
* @param {string} seriesName
*/
function metricComparison(metric, seriesName) {
var from_metric = $scope.fromJob._cache[metric] =
$scope.fromJob._cache[metric] || JPM.metrics(fromJobCond, metric, from_startTime, from_endTime);
var to_metric = $scope.toJob._cache[metric] =
$scope.toJob._cache[metric] || JPM.metrics(toJobCond, metric, to_startTime, to_endTime);
var holder = {};
$q.all([from_metric._promise, to_metric._promise]).then(function () {
from_metric = JPM.metricsToInterval(from_metric, 1000 * 60);
to_metric = JPM.metricsToInterval(to_metric, 1000 * 60);
var series_from = JPM.metricsToSeries("from job " + seriesName, from_metric, true);
var series_to = JPM.metricsToSeries("to job " + seriesName, to_metric, true);
holder.categories = intervalCategories(from_metric, to_metric);
holder.series = [series_from, series_to];
});
return holder;
}
// Dashboard1: Containers metrics
$scope.comparisonChart_Container = metricComparison("hadoop.job.runningcontainers", "running containers");
// Dashboard 2: Allocated
$scope.comparisonChart_allocatedMB = metricComparison("hadoop.job.allocatedmb", "allocated MB");
// Dashboard 3: vCores
$scope.comparisonChart_vCores = metricComparison("hadoop.job.allocatedvcores", "vCores");
// Dashboard 4: Task distribution
var from_distributionPromise = $scope.fromJob._cache.distributionPromise =
$scope.fromJob._cache.distributionPromise || taskDistribution($scope.fromJob.tags.jobId);
var to_distributionPromise = $scope.toJob._cache.distributionPromise =
$scope.toJob._cache.distributionPromise || taskDistribution($scope.toJob.tags.jobId);
var comparisonChart_taskDistribution = $scope.comparisonChart_taskDistribution = {
categories: taskDistributionCategory
};
$q.all([from_distributionPromise, to_distributionPromise]).then(function (args) {
var from_data = args[0];
var to_data = args[1];
var from_taskSeries = $.map(from_data.taskSeries, function (series) {
return $.extend({}, series, {name: "From " + series.name});
});
var to_taskSeries = $.map(to_data.taskSeries, function (series) {
return $.extend({}, series, {name: "To " + series.name});
});
comparisonChart_taskDistribution.series = from_taskSeries.concat(to_taskSeries);
});
}
// ======================== Job Refresh =========================
function jobRefresh() {
$scope.fromJob = findJob($wrapState.param.from);
$scope.toJob = findJob($wrapState.param.to);
$(window).resize();
refreshTrendMarkPoint();
refreshComparisonDashboard();
}
// ======================= Initialization =======================
jobRefresh();
$scope.$on('$locationChangeSuccess', function() {
if(browserAction) {
jobRefresh();
}
});
});
});
});
})();