azkaban-memoizeit
Changes
src/tl/flowsummary.tl 15(+0 -15)
src/web/js/azkaban.flow.stats.view.js 156(+156 -0)
src/web/js/azkaban.flow.view.js 135(+14 -121)
Details
diff --git a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
index 7346df6..f8c4281 100644
--- a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
@@ -104,6 +104,7 @@
<li id="graphViewLink"><a href="#graph">Graph</a></li>
<li id="jobslistViewLink"><a href="#jobslist">Job List</a></li>
<li id="flowLogViewLink"><a href="#log">Flow Log</a></li>
+ <li id="statsLink"><a href="#stats">Stats</a></li>
<li class="nav-button pull-right"><button type="button" id="pausebtn" class="btn btn-primary">Pause</button></li>
<li class="nav-button pull-right"><button type="button" id="resumebtn" class="btn btn-primary">Resume</button></li>
<li class="nav-button pull-right"><button type="button" id="cancelbtn" class="btn btn-danger">Cancel</button></li>
@@ -160,7 +161,14 @@
</div><!-- /.log-viewer -->
</div><!-- /.col-xs-12 -->
</div><!-- /.row -->
- </div><!-- /. -->
+ </div><!-- /.container-full -->
+
+ ## Stats view.
+
+ <div class="container-full" id="statsView">
+ <div class="row" id="stats-view-content">
+ </div><!-- /.row -->
+ </div><!-- /.container-fill -->
## Error message message dialog.
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index aa75aaf..89cdcf9 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -34,6 +34,7 @@
<script type="text/javascript" src="${context}/js/azkaban.common.utils.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.layout.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.flow.view.js"></script>
+ <script type="text/javascript" src="${context}/js/azkaban.flow.stats.view.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.flow.job.view.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.flow.graph.view.js"></script>
<script type="text/javascript" src="${context}/js/svgNavigate.js"></script>
@@ -140,9 +141,25 @@
<div class="container-full" id="summaryView">
<div class="row" id="summary-view-content">
- </div><!-- /.row -->
+ </div><!-- /.row -->
+ <div class="row" id="last-run-stats">
+ <div class="col-xs-12">
+ <h3>Last Run Stats</h3>
+ <div id="flow-stats-container">
+ <div class="alert alert-info">
+ <h4>Analyze last run</h4>
+ <p>Analyze the last run for aggregate performance statistics. <strong>Note:</strong> this may take a few minutes, especially if your flow is large.</p>
+ <p>
+ <button type="button" id="analyze-btn" class="btn btn-primary">Analyze</button>
+ </p>
+ </div>
+ </div>
+ </div><!-- /.col-lg-12 -->
+ </div>
</div><!-- /.container-fill -->
+ ## Context menu and the rest of the page.
+
<div class="container-full">
<div id="contextMenu">
</div>
diff --git a/src/java/azkaban/webapp/servlet/velocity/projectpage.vm b/src/java/azkaban/webapp/servlet/velocity/projectpage.vm
index 2958199..3acf1dd 100644
--- a/src/java/azkaban/webapp/servlet/velocity/projectpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/projectpage.vm
@@ -85,7 +85,7 @@
</div>
#end
#else
- <div class="alert alert-info">
+ <div class="alert alert-default">
<h4>No Flows</h4>
<p>No flows have been uploaded to this project yet.</p>
</div>
src/tl/flowsummary.tl 15(+0 -15)
diff --git a/src/tl/flowsummary.tl b/src/tl/flowsummary.tl
index 84ea565..f9e8b84 100644
--- a/src/tl/flowsummary.tl
+++ b/src/tl/flowsummary.tl
@@ -63,18 +63,3 @@
</div>
{/schedule}
</div>
-
- <div class="col-xs-12">
- <h3>Last Run Stats</h3>
- <div id="last-run-container">
- <div class="alert alert-info" id="analyze-last-run">
- <h4>Analyze last run</h4>
- <p>Analyze the last run for aggregate performance statistics. <strong>Note:</strong> this may take a few minutes, especially if your flow is large.</p>
- <p>
- <button type="button" id="analyze-btn" class="btn btn-primary">Analyze</button>
- </p>
- </div>
- </div>
- </div><!-- /.col-lg-12 -->
-
-
src/web/js/azkaban.flow.stats.view.js 156(+156 -0)
diff --git a/src/web/js/azkaban.flow.stats.view.js b/src/web/js/azkaban.flow.stats.view.js
new file mode 100644
index 0000000..5f48edc
--- /dev/null
+++ b/src/web/js/azkaban.flow.stats.view.js
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2012 LinkedIn Corp.
+ *
+ * Licensed 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.
+ */
+
+$.namespace('azkaban');
+
+azkaban.FlowStatsModel = Backbone.Model.extend({});
+azkaban.FlowStatsView = Backbone.View.extend({
+ events: {
+ },
+
+ initialize: function(settings) {
+ this.model.bind('change:view', this.handleChangeView, this);
+ this.model.bind('render', this.render, this);
+ },
+
+ render: function(evt) {
+ },
+
+ show: function(execId) {
+ this.analyzeExecution(execId);
+ },
+
+ fetchJobs: function(execId) {
+ var requestURL = contextURL + "/executor";
+ var requestData = {"execid": execId, "ajax":"fetchexecflow"};
+ var jobs = [];
+ var successHandler = function(data) {
+ for (var i = 0; i < data.nodes.length; ++i) {
+ var node = data.nodes[i];
+ jobs.push(node.id);
+ }
+ };
+ $.ajax({
+ url: requestURL,
+ data: requestData,
+ success: successHandler,
+ dataType: "json",
+ async: false
+ });
+ return jobs;
+ },
+
+ fetchJobStats: function(jobId, execId) {
+ var requestURL = contextURL + "/executor";
+ var requestData = {
+ "execid": execId,
+ "flowid": flowId,
+ "jobid": jobId,
+ "ajax": "fetchExecJobStats"
+ };
+ var stats = null;
+ var successHandler = function(data) {
+ stats = data;
+ };
+ $.ajax({
+ url: requestURL,
+ data: requestData,
+ success: successHandler,
+ dataType: "json",
+ async: false
+ });
+ return stats;
+ },
+
+ updateStats: function(jobStats, data) {
+ var aggregateStats = data.stats;
+ var state = jobStats.state;
+ var conf = jobStats.conf;
+ var mappers = parseInt(state.totalMappers);
+ var reducers = parseInt(state.totalReducers);
+ if (mappers > aggregateStats.maxMapSlots) {
+ aggregateStats.maxMapSlots = mappers;
+ }
+ if (reducers > aggregateStats.maxReduceSlots) {
+ aggregateStats.maxReduceSlots = reducers;
+ }
+ aggregateStats.totalMapSlots += mappers;
+ aggregateStats.totalReduceSlots += reducers;
+ },
+
+ analyzeExecution: function(execId) {
+ var jobs = this.fetchJobs(execId);
+ if (jobs == null) {
+ this.model.set({'data': null});
+ this.model.trigger('render');
+ return;
+ }
+
+ var data = {
+ success: false,
+ message: null,
+ warnings: [],
+ stats: {
+ maxMapSlots: 0,
+ maxReduceSlots: 0,
+ totalMapSlots: 0,
+ totalReduceSlots: 0,
+ numJobs: jobs.length,
+ longestTaskTime: 0
+ }
+ };
+
+ for (var i = 0; i < jobs.length; ++i) {
+ var job = jobs[i];
+ var jobStats = this.fetchJobStats(job, execId);
+ if (jobStats.jobStats == null) {
+ data.warnings.push("No job stats available for job " + job.id);
+ continue;
+ }
+ for (var j = 0; j < jobStats.jobStats.length; ++j) {
+ this.updateStats(jobStats.jobStats[j], data);
+ }
+ }
+ data.success = true;
+ this.model.set({'data': data});
+ this.model.trigger('render');
+ },
+
+ render: function(evt) {
+ var view = this;
+ var data = this.model.get('data');
+ if (data == null) {
+ var msg = { message: "Error retrieving flow stats."};
+ dust.render("flowsummary-no-data", msg, function(err, out) {
+ view.display(out);
+ });
+ }
+ else if (data.success == "false") {
+ dust.render("flowsummary-no-data", data, function(err, out) {
+ view.display(out);
+ });
+ }
+ else {
+ dust.render("flowsummary-last-run", data, function(err, out) {
+ view.display(out);
+ });
+ }
+ },
+
+ display: function(out) {
+ $('#flow-stats-container').html(out);
+ },
+});
src/web/js/azkaban.flow.view.js 135(+14 -121)
diff --git a/src/web/js/azkaban.flow.view.js b/src/web/js/azkaban.flow.view.js
index 959dc4a..028a07b 100644
--- a/src/web/js/azkaban.flow.view.js
+++ b/src/web/js/azkaban.flow.view.js
@@ -347,132 +347,16 @@ azkaban.SummaryView = Backbone.View.extend({
var view = this;
var successHandler = function(data) {
if (data.success == "false" || data.execId == null) {
- view.renderLastRun(data);
+ dust.render("flowsummary-no-data", data, function(err, out) {
+ $('#flow-stats-container').html(out);
+ });
return;
}
- view.analyzeLastRun(data.execId);
+ flowStatsView.show(data.execId);
};
$.get(requestURL, requestData, successHandler, 'json');
},
- fetchJobs: function(execId) {
- var requestURL = contextURL + "/executor";
- var requestData = {"execid": execId, "ajax":"fetchexecflow"};
- var jobs = [];
- var successHandler = function(data) {
- for (var i = 0; i < data.nodes.length; ++i) {
- var node = data.nodes[i];
- jobs.push(node.id);
- }
- };
- $.ajax({
- url: requestURL,
- data: requestData,
- success: successHandler,
- dataType: "json",
- async: false
- });
- return jobs;
- },
-
- fetchJobStats: function(jobId, execId) {
- var requestURL = contextURL + "/executor";
- var requestData = {
- "execid": execId,
- "flowid": flowId,
- "jobid": jobId,
- "ajax": "fetchExecJobStats"
- };
- var stats = null;
- var successHandler = function(data) {
- stats = data;
- };
- $.ajax({
- url: requestURL,
- data: requestData,
- success: successHandler,
- dataType: "json",
- async: false
- });
- return stats;
- },
-
- updateStats: function(jobStats, data) {
- var aggregateStats = data.stats;
- var state = jobStats.state;
- var conf = jobStats.conf;
- var mappers = parseInt(state.totalMappers);
- var reducers = parseInt(state.totalReducers);
- if (mappers > aggregateStats.maxMapSlots) {
- aggregateStats.maxMapSlots = mappers;
- }
- if (reducers > aggregateStats.maxReduceSlots) {
- aggregateStats.maxReduceSlots = reducers;
- }
- aggregateStats.totalMapSlots += mappers;
- aggregateStats.totalReduceSlots += reducers;
- },
-
- analyzeLastRun: function(execId) {
- var jobs = this.fetchJobs(execId);
- if (jobs == null) {
- this.renderLastRun(null);
- return;
- }
-
- var data = {
- success: false,
- message: null,
- warnings: [],
- stats: {
- maxMapSlots: 0,
- maxReduceSlots: 0,
- totalMapSlots: 0,
- totalReduceSlots: 0,
- numJobs: jobs.length,
- longestTaskTime: 0
- }
- };
-
- for (var i = 0; i < jobs.length; ++i) {
- var job = jobs[i];
- var jobStats = this.fetchJobStats(job, execId);
- if (jobStats.jobStats == null) {
- data.warnings.push("No job stats available for job " + job.id);
- continue;
- }
- for (var j = 0; j < jobStats.jobStats.length; ++j) {
- this.updateStats(jobStats.jobStats[j], data);
- }
- }
- data.success = true;
- this.renderLastRun(data);
- },
-
- renderLastRun: function(data) {
- var view = this;
- if (data == null) {
- var msg = { message: "Error retrieving last run data."};
- dust.render("flowsummary-no-data", msg, function(err, out) {
- view.displayLastRun(out);
- });
- }
- else if (data.success == "false") {
- dust.render("flowsummary-no-data", data, function(err, out) {
- view.displayLastRun(out);
- });
- }
- else {
- dust.render("flowsummary-last-run", data, function(err, out) {
- view.displayLastRun(out);
- });
- }
- },
-
- displayLastRun: function(out) {
- $('#last-run-container').html(out);
- },
-
handleChangeView: function(evt) {
},
@@ -544,6 +428,9 @@ azkaban.ExecutionModel = Backbone.Model.extend({});
var summaryModel;
azkaban.SummaryModel = Backbone.Model.extend({});
+var flowStatsView;
+var flowStatsModel;
+
var mainSvgGraphView;
$(function() {
@@ -560,7 +447,13 @@ $(function() {
el: $('#summaryView'),
model: summaryModel
});
-
+
+ flowStatsModel = new azkaban.FlowStatsModel();
+ flowStatsView = new azkaban.FlowStatsView({
+ el: $('#last-run-stats'),
+ model: flowStatsModel
+ });
+
flowTabView = new azkaban.FlowTabView({
el: $('#headertabs'),
selectedView: selected