@@ -38,6 +38,7 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
3838 <th >Output Op Id </th >
3939 <th >Description </th >
4040 <th >Duration </th >
41+ <th >Status </th >
4142 <th >Job Id </th >
4243 <th >Duration </th >
4344 <th class =" sorttable_nosort" >Stages : Succeeded / Total </th >
@@ -49,18 +50,42 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
4950 outputOpId : OutputOpId ,
5051 outputOpDescription : Seq [Node ],
5152 formattedOutputOpDuration : String ,
53+ outputOpStatus : String ,
5254 numSparkJobRowsInOutputOp : Int ,
5355 isFirstRow : Boolean ,
5456 sparkJob : SparkJobIdWithUIData ): Seq [Node ] = {
5557 if (sparkJob.jobUIData.isDefined) {
5658 generateNormalJobRow(outputOpId, outputOpDescription, formattedOutputOpDuration,
57- numSparkJobRowsInOutputOp, isFirstRow, sparkJob.jobUIData.get)
59+ outputOpStatus, numSparkJobRowsInOutputOp, isFirstRow, sparkJob.jobUIData.get)
5860 } else {
5961 generateDroppedJobRow(outputOpId, outputOpDescription, formattedOutputOpDuration,
60- numSparkJobRowsInOutputOp, isFirstRow, sparkJob.sparkJobId)
62+ outputOpStatus, numSparkJobRowsInOutputOp, isFirstRow, sparkJob.sparkJobId)
6163 }
6264 }
6365
66+ private def generateOutputOpRowWithoutSparkJobs (
67+ outputOpId : OutputOpId ,
68+ outputOpDescription : Seq [Node ],
69+ formattedOutputOpDuration : String ,
70+ outputOpStatus : String ): Seq [Node ] = {
71+ <tr >
72+ <td class =" output-op-id-cell" >{outputOpId.toString}</td >
73+ <td >{outputOpDescription}</td >
74+ <td >{formattedOutputOpDuration}</td >
75+ {outputOpStatusCell(outputOpStatus, rowspan = 1 )}
76+ <!-- Job Id -->
77+ <td >-</ td>
78+ <!-- Duration -->
79+ <td >-</ td>
80+ <!-- Stages : Succeeded / Total -->
81+ <td >-</ td>
82+ <!-- Tasks (for all stages): Succeeded / Total -->
83+ <td >-</ td>
84+ <!-- Error -->
85+ <td >-</ td>
86+ </tr >
87+ }
88+
6489 /**
6590 * Generate a row for a Spark Job. Because duplicated output op infos needs to be collapsed into
6691 * one cell, we use "rowspan" for the first row of a output op.
@@ -69,6 +94,7 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
6994 outputOpId : OutputOpId ,
7095 outputOpDescription : Seq [Node ],
7196 formattedOutputOpDuration : String ,
97+ outputOpStatus : String ,
7298 numSparkJobRowsInOutputOp : Int ,
7399 isFirstRow : Boolean ,
74100 sparkJob : JobUIData ): Seq [Node ] = {
@@ -94,7 +120,8 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
94120 <td rowspan ={numSparkJobRowsInOutputOp.toString}>
95121 {outputOpDescription}
96122 </td >
97- <td rowspan ={numSparkJobRowsInOutputOp.toString}>{formattedOutputOpDuration}</td >
123+ <td rowspan ={numSparkJobRowsInOutputOp.toString}>{formattedOutputOpDuration}</td > ++
124+ {outputOpStatusCell(outputOpStatus, numSparkJobRowsInOutputOp)}
98125 } else {
99126 Nil
100127 }
@@ -125,7 +152,7 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
125152 total = sparkJob.numTasks - sparkJob.numSkippedTasks)
126153 }
127154 </td >
128- {failureReasonCell(lastFailureReason)}
155+ {failureReasonCell(lastFailureReason, rowspan = 1 )}
129156 </tr >
130157 }
131158
@@ -137,6 +164,7 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
137164 outputOpId : OutputOpId ,
138165 outputOpDescription : Seq [Node ],
139166 formattedOutputOpDuration : String ,
167+ outputOpStatus : String ,
140168 numSparkJobRowsInOutputOp : Int ,
141169 isFirstRow : Boolean ,
142170 jobId : Int ): Seq [Node ] = {
@@ -147,7 +175,8 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
147175 if (isFirstRow) {
148176 <td class =" output-op-id-cell" rowspan ={numSparkJobRowsInOutputOp.toString}>{outputOpId.toString}</td >
149177 <td rowspan ={numSparkJobRowsInOutputOp.toString}>{outputOpDescription}</td >
150- <td rowspan ={numSparkJobRowsInOutputOp.toString}>{formattedOutputOpDuration}</td >
178+ <td rowspan ={numSparkJobRowsInOutputOp.toString}>{formattedOutputOpDuration}</td > ++
179+ {outputOpStatusCell(outputOpStatus, numSparkJobRowsInOutputOp)}
151180 } else {
152181 Nil
153182 }
@@ -156,7 +185,7 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
156185 <tr >
157186 {prefixCells}
158187 <td sorttable_customkey ={jobId.toString}>
159- {jobId.toString}
188+ {if ( jobId >= 0 ) jobId .toString else " - " }
160189 </td >
161190 <!-- Duration -->
162191 <td >-</ td>
@@ -170,7 +199,9 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
170199 }
171200
172201 private def generateOutputOpIdRow (
173- outputOpId : OutputOpId , sparkJobs : Seq [SparkJobIdWithUIData ]): Seq [Node ] = {
202+ outputOpId : OutputOpId ,
203+ outputOpStatus : String ,
204+ sparkJobs : Seq [SparkJobIdWithUIData ]): Seq [Node ] = {
174205 // We don't count the durations of dropped jobs
175206 val sparkJobDurations = sparkJobs.filter(_.jobUIData.nonEmpty).map(_.jobUIData.get).
176207 map(sparkJob => {
@@ -189,12 +220,32 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
189220
190221 val description = generateOutputOpDescription(sparkJobs)
191222
192- generateJobRow(
193- outputOpId, description, formattedOutputOpDuration, sparkJobs.size, true , sparkJobs.head) ++
194- sparkJobs.tail.map { sparkJob =>
223+ if (sparkJobs.isEmpty) {
224+ generateOutputOpRowWithoutSparkJobs(
225+ outputOpId, description, formattedOutputOpDuration, outputOpStatus)
226+ } else {
227+ val firstRow =
195228 generateJobRow(
196- outputOpId, description, formattedOutputOpDuration, sparkJobs.size, false , sparkJob)
197- }.flatMap(x => x)
229+ outputOpId,
230+ description,
231+ formattedOutputOpDuration,
232+ outputOpStatus,
233+ sparkJobs.size,
234+ true ,
235+ sparkJobs.head)
236+ val tailRows =
237+ sparkJobs.tail.map { sparkJob =>
238+ generateJobRow(
239+ outputOpId,
240+ description,
241+ formattedOutputOpDuration,
242+ outputOpStatus,
243+ sparkJobs.size,
244+ false ,
245+ sparkJob)
246+ }
247+ (firstRow ++ tailRows).flatten
248+ }
198249 }
199250
200251 private def generateOutputOpDescription (sparkJobs : Seq [SparkJobIdWithUIData ]): Seq [Node ] = {
@@ -228,7 +279,7 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
228279 }
229280 }
230281
231- private def failureReasonCell (failureReason : String ): Seq [Node ] = {
282+ private def failureReasonCell (failureReason : String , rowspan : Int ): Seq [Node ] = {
232283 val isMultiline = failureReason.indexOf('\n ' ) >= 0
233284 // Display the first line by default
234285 val failureReasonSummary = StringEscapeUtils .escapeHtml4(
@@ -250,7 +301,14 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
250301 } else {
251302 " "
252303 }
253- <td valign =" middle" style =" max-width: 300px" >{failureReasonSummary}{details}</td >
304+
305+ if (rowspan == 1 ) {
306+ <td valign =" middle" style =" max-width: 300px" >{failureReasonSummary}{details}</td >
307+ } else {
308+ <td valign =" middle" style =" max-width: 300px" rowspan ={rowspan.toString}>
309+ {failureReasonSummary}{details}
310+ </td >
311+ }
254312 }
255313
256314 private def getJobData (sparkJobId : SparkJobId ): Option [JobUIData ] = {
@@ -265,16 +323,20 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
265323 * Generate the job table for the batch.
266324 */
267325 private def generateJobTable (batchUIData : BatchUIData ): Seq [Node ] = {
268- val outputOpIdToSparkJobIds = batchUIData.outputOpIdSparkJobIdPairs.groupBy(_.outputOpId).toSeq.
269- sortBy(_._1). // sorted by OutputOpId
326+ val outputOpIdToSparkJobIds = batchUIData.outputOpIdSparkJobIdPairs.groupBy(_.outputOpId).
270327 map { case (outputOpId, outputOpIdAndSparkJobIds) =>
271328 // sort SparkJobIds for each OutputOpId
272329 (outputOpId, outputOpIdAndSparkJobIds.map(_.sparkJobId).sorted)
273330 }
331+ val outputOps = (0 until batchUIData.numOutputOp).map { outputOpId =>
332+ val status = batchUIData.failureReason.getOrElse(outputOpId, " Succeeded" )
333+ val sparkJobIds = outputOpIdToSparkJobIds.getOrElse(outputOpId, Seq .empty)
334+ (outputOpId, status, sparkJobIds)
335+ }
274336 sparkListener.synchronized {
275- val outputOpIdWithJobs : Seq [(OutputOpId , Seq [SparkJobIdWithUIData ])] =
276- outputOpIdToSparkJobIds .map { case (outputOpId, sparkJobIds) =>
277- (outputOpId,
337+ val outputOpIdWithJobs : Seq [(OutputOpId , String , Seq [SparkJobIdWithUIData ])] =
338+ outputOps .map { case (outputOpId, status , sparkJobIds) =>
339+ (outputOpId, status,
278340 sparkJobIds.map(sparkJobId => SparkJobIdWithUIData (sparkJobId, getJobData(sparkJobId))))
279341 }
280342
@@ -285,7 +347,8 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
285347 <tbody >
286348 {
287349 outputOpIdWithJobs.map {
288- case (outputOpId, sparkJobIds) => generateOutputOpIdRow(outputOpId, sparkJobIds)
350+ case (outputOpId, status, sparkJobIds) =>
351+ generateOutputOpIdRow(outputOpId, status, sparkJobIds)
289352 }
290353 }
291354 </tbody >
@@ -386,4 +449,12 @@ private[ui] class BatchPage(parent: StreamingTab) extends WebUIPage("batch") {
386449 Unparsed (StringEscapeUtils .escapeHtml4(metadataDescription).
387450 replaceAllLiterally(" \t " , " " ).replaceAllLiterally(" \n " , " <br/>" ))
388451 }
452+
453+ private def outputOpStatusCell (status : String , rowspan : Int ): Seq [Node ] = {
454+ if (status == " Succeeded" ) {
455+ <td rowspan ={rowspan.toString}>Succeeded </td >
456+ } else {
457+ failureReasonCell(status, rowspan)
458+ }
459+ }
389460}
0 commit comments