Skip to content

Commit a475ea1

Browse files
committed
Add progress bars to jobs page.
1 parent 45343b8 commit a475ea1

File tree

5 files changed

+67
-18
lines changed

5 files changed

+67
-18
lines changed

core/src/main/scala/org/apache/spark/ui/UIUtils.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,4 +283,17 @@ private[spark] object UIUtils extends Logging {
283283
</tbody>
284284
</table>
285285
}
286+
287+
def makeProgressBar(started: Int, completed: Int, failed: Int, total: Int): Seq[Node] = {
288+
val completeWidth = "width: %s%%".format((completed.toDouble/total)*100)
289+
val startWidth = "width: %s%%".format((started.toDouble/total)*100)
290+
291+
<div class="progress">
292+
<span style="text-align:center; position:absolute; width:100%; left:0;">
293+
{completed}/{total} { if (failed > 0) s"($failed failed)" else "" }
294+
</span>
295+
<div class="bar bar-completed" style={completeWidth}></div>
296+
<div class="bar bar-running" style={startWidth}></div>
297+
</div>
298+
}
286299
}

core/src/main/scala/org/apache/spark/ui/jobs/AllJobsPage.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
4444
<th>Description</th>
4545
<th>Submitted</th>
4646
<th>Duration</th>
47+
<th>Tasks: Succeeded/Total</th>
4748
}
4849

4950
def makeRow(job: JobUIData): Seq[Node] = {
@@ -74,6 +75,10 @@ private[ui] class AllJobsPage(parent: JobsTab) extends WebUIPage("") {
7475
{formattedSubmissionTime}
7576
</td>
7677
<td sorttable_customkey={duration.getOrElse(-1).toString}>{formattedDuration}</td>
78+
<td class="progress-cell">
79+
{UIUtils.makeProgressBar(job.numActiveTasks, job.numCompletedTasks,
80+
job.numFailedTasks, job.numTasks)}
81+
</td>
7782
</tr>
7883
}
7984

core/src/main/scala/org/apache/spark/ui/jobs/JobProgressListener.scala

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
package org.apache.spark.ui.jobs
1919

20-
import scala.collection.mutable.{HashMap, ListBuffer}
20+
import scala.collection.mutable.{HashMap, HashSet, ListBuffer}
2121

2222
import org.apache.spark._
2323
import org.apache.spark.annotation.DeveloperApi
@@ -59,7 +59,8 @@ class JobProgressListener(conf: SparkConf) extends SparkListener with Logging {
5959
val failedStages = ListBuffer[StageInfo]()
6060
val stageIdToData = new HashMap[(StageId, StageAttemptId), StageUIData]
6161
val stageIdToInfo = new HashMap[StageId, StageInfo]
62-
62+
val stageIdToActiveJobIds = new HashMap[StageId, HashSet[JobId]]
63+
6364
// Number of completed and failed stages, may not actually equal to completedStages.size and
6465
// failedStages.size respectively due to completedStage and failedStages only maintain the latest
6566
// part of the stages, the earlier ones will be removed when there are too many stages for
@@ -86,6 +87,9 @@ class JobProgressListener(conf: SparkConf) extends SparkListener with Logging {
8687
jobGroup, JobExecutionStatus.RUNNING)
8788
jobIdToData(jobStart.jobId) = jobData
8889
activeJobs(jobStart.jobId) = jobData
90+
for (stageId <- jobStart.stageIds) {
91+
stageIdToActiveJobIds.getOrElseUpdate(stageId, new HashSet[StageId]).add(jobStart.jobId)
92+
}
8993
}
9094

9195
override def onJobEnd(jobEnd: SparkListenerJobEnd) = synchronized {
@@ -102,6 +106,9 @@ class JobProgressListener(conf: SparkConf) extends SparkListener with Logging {
102106
failedJobs += jobData
103107
jobData.status = JobExecutionStatus.FAILED
104108
}
109+
for (stageId <- jobData.stageIds) {
110+
stageIdToActiveJobIds.get(stageId).foreach(_.remove(jobEnd.jobId))
111+
}
105112
}
106113

107114
override def onStageCompleted(stageCompleted: SparkListenerStageCompleted) = synchronized {
@@ -138,6 +145,7 @@ class JobProgressListener(conf: SparkConf) extends SparkListener with Logging {
138145
stages.take(toRemove).foreach { s =>
139146
stageIdToData.remove((s.stageId, s.attemptId))
140147
stageIdToInfo.remove(s.stageId)
148+
stageIdToActiveJobIds.remove(s.stageId)
141149
}
142150
stages.trimStart(toRemove)
143151
}
@@ -162,6 +170,14 @@ class JobProgressListener(conf: SparkConf) extends SparkListener with Logging {
162170

163171
val stages = poolToActiveStages.getOrElseUpdate(poolName, new HashMap[Int, StageInfo])
164172
stages(stage.stageId) = stage
173+
174+
for (
175+
activeJobsDependentOnStage <- stageIdToActiveJobIds.get(stage.stageId);
176+
jobId <- activeJobsDependentOnStage;
177+
jobData <- jobIdToData.get(jobId)
178+
) {
179+
jobData.numTasks += stage.numTasks
180+
}
165181
}
166182

167183
override def onTaskStart(taskStart: SparkListenerTaskStart) = synchronized {
@@ -174,6 +190,13 @@ class JobProgressListener(conf: SparkConf) extends SparkListener with Logging {
174190
stageData.numActiveTasks += 1
175191
stageData.taskData.put(taskInfo.taskId, new TaskUIData(taskInfo))
176192
}
193+
for (
194+
activeJobsDependentOnStage <- stageIdToActiveJobIds.get(taskStart.stageId);
195+
jobId <- activeJobsDependentOnStage;
196+
jobData <- jobIdToData.get(jobId)
197+
) {
198+
jobData.numActiveTasks += 1
199+
}
177200
}
178201

179202
override def onTaskGettingResult(taskGettingResult: SparkListenerTaskGettingResult) {
@@ -208,6 +231,8 @@ class JobProgressListener(conf: SparkConf) extends SparkListener with Logging {
208231
execSummary.taskTime += info.duration
209232
stageData.numActiveTasks -= 1
210233

234+
val isRecomputation = stageData.completedIndices.contains(info.index)
235+
211236
val (errorMessage, metrics): (Option[String], Option[TaskMetrics]) =
212237
taskEnd.reason match {
213238
case org.apache.spark.Success =>
@@ -231,6 +256,22 @@ class JobProgressListener(conf: SparkConf) extends SparkListener with Logging {
231256
taskData.taskInfo = info
232257
taskData.taskMetrics = metrics
233258
taskData.errorMessage = errorMessage
259+
260+
for (
261+
activeJobsDependentOnStage <- stageIdToActiveJobIds.get(taskEnd.stageId);
262+
jobId <- activeJobsDependentOnStage;
263+
jobData <- jobIdToData.get(jobId)
264+
) {
265+
jobData.numActiveTasks -= 1
266+
taskEnd.reason match {
267+
case Success =>
268+
if (!isRecomputation) {
269+
jobData.numCompletedTasks += 1
270+
}
271+
case _ =>
272+
jobData.numFailedTasks += 1
273+
}
274+
}
234275
}
235276
}
236277

core/src/main/scala/org/apache/spark/ui/jobs/StageTable.scala

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,6 @@ private[ui] class StageTableBase(
6969
</table>
7070
}
7171

72-
private def makeProgressBar(started: Int, completed: Int, failed: Int, total: Int): Seq[Node] =
73-
{
74-
val completeWidth = "width: %s%%".format((completed.toDouble/total)*100)
75-
val startWidth = "width: %s%%".format((started.toDouble/total)*100)
76-
77-
<div class="progress">
78-
<span style="text-align:center; position:absolute; width:100%; left:0;">
79-
{completed}/{total} { if (failed > 0) s"($failed failed)" else "" }
80-
</span>
81-
<div class="bar bar-completed" style={completeWidth}></div>
82-
<div class="bar bar-running" style={startWidth}></div>
83-
</div>
84-
}
85-
8672
private def makeDescription(s: StageInfo): Seq[Node] = {
8773
// scalastyle:off
8874
val killLink = if (killEnabled) {
@@ -172,7 +158,7 @@ private[ui] class StageTableBase(
172158
<td valign="middle">{submissionTime}</td>
173159
<td sorttable_customkey={duration.getOrElse(-1).toString}>{formattedDuration}</td>
174160
<td class="progress-cell">
175-
{makeProgressBar(stageData.numActiveTasks, stageData.completedIndices.size,
161+
{UIUtils.makeProgressBar(stageData.numActiveTasks, stageData.completedIndices.size,
176162
stageData.numFailedTasks, s.numTasks)}
177163
</td>
178164
<td sorttable_customkey={inputRead.toString}>{inputReadWithUnit}</td>

core/src/main/scala/org/apache/spark/ui/jobs/UIData.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ private[jobs] object UIData {
4343
var endTime: Option[Long] = None,
4444
var stageIds: Seq[Int] = Seq.empty,
4545
var jobGroup: Option[String] = None,
46-
var status: JobExecutionStatus = JobExecutionStatus.UNKNOWN
46+
var status: JobExecutionStatus = JobExecutionStatus.UNKNOWN,
47+
var numTasks: Int = 0,
48+
var numActiveTasks: Int = 0,
49+
var numCompletedTasks: Int = 0,
50+
var numFailedTasks: Int = 0
4751
)
4852

4953
class StageUIData {

0 commit comments

Comments
 (0)