Skip to content

Commit 318525a

Browse files
twinkle-gMarcelo Vanzin
authored andcommitted
SPARK-4705: 1) moved from directory structure to single file, as per the master branch. 2) Added the attempt id inside the SparkListenerApplicationStart, to make the info available independent of directory structure. 3) Changes in History Server to render the UI as per the snaphot II
1 parent 6b2e521 commit 318525a

File tree

8 files changed

+157
-25
lines changed

8 files changed

+157
-25
lines changed

core/src/main/scala/org/apache/spark/SparkContext.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1757,7 +1757,7 @@ class SparkContext(config: SparkConf) extends Logging with ExecutorAllocationCli
17571757
// Note: this code assumes that the task scheduler has been initialized and has contacted
17581758
// the cluster manager to get an application ID (in case the cluster manager provides one).
17591759
listenerBus.post(SparkListenerApplicationStart(appName, Some(applicationId),
1760-
startTime, sparkUser))
1760+
startTime, sparkUser, applicationAttemptId))
17611761
}
17621762

17631763
/** Post the application end event */

core/src/main/scala/org/apache/spark/deploy/history/ApplicationHistoryProvider.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ private[history] case class ApplicationHistoryInfo(
2626
endTime: Long,
2727
lastUpdated: Long,
2828
sparkUser: String,
29-
completed: Boolean = false)
29+
completed: Boolean = false,
30+
appAttemptId: String = "")
3031

3132
private[history] abstract class ApplicationHistoryProvider {
3233

core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,13 @@ private[history] class FsHistoryProvider(conf: SparkConf) extends ApplicationHis
248248
if (!mergedApps.contains(info.id) ||
249249
mergedApps(info.id).logPath.endsWith(EventLoggingListener.IN_PROGRESS) &&
250250
!info.logPath.endsWith(EventLoggingListener.IN_PROGRESS)) {
251-
mergedApps += (info.id -> info)
251+
val key =
252+
if (info.appAttemptId.equals("")) {
253+
info.id
254+
} else {
255+
info.id + "_" + info.appAttemptId
256+
}
257+
mergedApps += (key -> info)
252258
}
253259
}
254260

@@ -343,7 +349,8 @@ private[history] class FsHistoryProvider(conf: SparkConf) extends ApplicationHis
343349
appListener.endTime.getOrElse(-1L),
344350
getModificationTime(eventLog).get,
345351
appListener.sparkUser.getOrElse(NOT_STARTED),
346-
isApplicationCompleted(eventLog))
352+
isApplicationCompleted(eventLog),
353+
appListener.appAttemptId.getOrElse(""))
347354
} finally {
348355
logInput.close()
349356
}
@@ -438,5 +445,7 @@ private class FsApplicationHistoryInfo(
438445
endTime: Long,
439446
lastUpdated: Long,
440447
sparkUser: String,
441-
completed: Boolean = true)
442-
extends ApplicationHistoryInfo(id, name, startTime, endTime, lastUpdated, sparkUser, completed)
448+
completed: Boolean = true,
449+
appAttemptId: String ="")
450+
extends ApplicationHistoryInfo(
451+
id, name, startTime, endTime, lastUpdated, sparkUser, completed, appAttemptId)

core/src/main/scala/org/apache/spark/deploy/history/HistoryPage.scala

Lines changed: 128 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import javax.servlet.http.HttpServletRequest
2222
import scala.xml.Node
2323

2424
import org.apache.spark.ui.{WebUIPage, UIUtils}
25+
import scala.collection.immutable.ListMap
26+
import scala.collection.mutable.HashMap
27+
import scala.collection.mutable.ArrayBuffer
2528

2629
private[history] class HistoryPage(parent: HistoryServer) extends WebUIPage("") {
2730

@@ -34,18 +37,31 @@ private[history] class HistoryPage(parent: HistoryServer) extends WebUIPage("")
3437
val requestedIncomplete =
3538
Option(request.getParameter("showIncomplete")).getOrElse("false").toBoolean
3639

37-
val allApps = parent.getApplicationList().filter(_.completed != requestedIncomplete)
38-
val actualFirst = if (requestedFirst < allApps.size) requestedFirst else 0
39-
val apps = allApps.slice(actualFirst, Math.min(actualFirst + pageSize, allApps.size))
40-
40+
val allCompletedAppsNAttempts =
41+
parent.getApplicationList().filter(_.completed != requestedIncomplete)
42+
val (hasAttemptInfo, appToAttemptMap) = getApplicationLevelList(allCompletedAppsNAttempts)
43+
44+
val allAppsSize = allCompletedAppsNAttempts.size
45+
46+
val actualFirst = if (requestedFirst < allAppsSize) requestedFirst else 0
47+
val apps =
48+
allCompletedAppsNAttempts.slice(actualFirst, Math.min(actualFirst + pageSize, allAppsSize))
49+
val appWithAttemptsDisplayList =
50+
appToAttemptMap.slice(actualFirst, Math.min(actualFirst + pageSize, allAppsSize))
51+
4152
val actualPage = (actualFirst / pageSize) + 1
42-
val last = Math.min(actualFirst + pageSize, allApps.size) - 1
43-
val pageCount = allApps.size / pageSize + (if (allApps.size % pageSize > 0) 1 else 0)
53+
val last = Math.min(actualFirst + pageSize, allAppsSize) - 1
54+
val pageCount = allAppsSize / pageSize + (if (allAppsSize % pageSize > 0) 1 else 0)
4455

4556
val secondPageFromLeft = 2
4657
val secondPageFromRight = pageCount - 1
4758

48-
val appTable = UIUtils.listingTable(appHeader, appRow, apps)
59+
val appTable =
60+
if (hasAttemptInfo) {
61+
UIUtils.listingTable(appWithAttemptHeader, appWithAttemptRow, appWithAttemptsDisplayList)
62+
} else {
63+
UIUtils.listingTable(appHeader, appRow, apps)
64+
}
4965
val providerConfig = parent.getProviderConfig()
5066
val content =
5167
<div class="row-fluid">
@@ -59,15 +75,15 @@ private[history] class HistoryPage(parent: HistoryServer) extends WebUIPage("")
5975
// to the first and last page. If the current page +/- `plusOrMinus` is greater
6076
// than the 2nd page from the first page or less than the 2nd page from the last
6177
// page, `...` will be displayed.
62-
if (allApps.size > 0) {
78+
if (allAppsSize > 0) {
6379
val leftSideIndices =
6480
rangeIndices(actualPage - plusOrMinus until actualPage, 1 < _, requestedIncomplete)
6581
val rightSideIndices =
6682
rangeIndices(actualPage + 1 to actualPage + plusOrMinus, _ < pageCount,
6783
requestedIncomplete)
6884

6985
<h4>
70-
Showing {actualFirst + 1}-{last + 1} of {allApps.size}
86+
Showing {actualFirst + 1}-{last + 1} of {allAppsSize}
7187
{if (requestedIncomplete) "(Incomplete applications)"}
7288
<span style="float: right">
7389
{
@@ -113,6 +129,36 @@ private[history] class HistoryPage(parent: HistoryServer) extends WebUIPage("")
113129
</div>
114130
UIUtils.basicSparkPage(content, "History Server")
115131
}
132+
133+
private def getApplicationLevelList (appNattemptList: Iterable[ApplicationHistoryInfo]) ={
134+
// Create HashMap as per the multiple attempts for one application.
135+
// If there is no attempt specific stuff, then
136+
// do return false, to indicate the same, so that previous UI gets displayed.
137+
var hasAttemptInfo = false
138+
val appToAttemptInfo = new HashMap[String, ArrayBuffer[ApplicationHistoryInfo]]
139+
for( appAttempt <- appNattemptList) {
140+
if(!appAttempt.appAttemptId.equals("")){
141+
hasAttemptInfo = true
142+
val attemptId = appAttempt.appAttemptId.toInt
143+
if(appToAttemptInfo.contains(appAttempt.id)){
144+
val currentAttempts = appToAttemptInfo.get(appAttempt.id).get
145+
currentAttempts += appAttempt
146+
appToAttemptInfo.put( appAttempt.id, currentAttempts)
147+
} else {
148+
val currentAttempts = new ArrayBuffer[ApplicationHistoryInfo]()
149+
currentAttempts += appAttempt
150+
appToAttemptInfo.put( appAttempt.id, currentAttempts )
151+
}
152+
}else {
153+
val currentAttempts = new ArrayBuffer[ApplicationHistoryInfo]()
154+
currentAttempts += appAttempt
155+
appToAttemptInfo.put(appAttempt.id, currentAttempts)
156+
}
157+
}
158+
val sortedMap = ListMap(appToAttemptInfo.toSeq.sortWith(_._1 > _._1):_*)
159+
(hasAttemptInfo, sortedMap)
160+
}
161+
116162

117163
private val appHeader = Seq(
118164
"App ID",
@@ -128,6 +174,16 @@ private[history] class HistoryPage(parent: HistoryServer) extends WebUIPage("")
128174
range.filter(condition).map(nextPage =>
129175
<a href={makePageLink(nextPage, showIncomplete)}> {nextPage} </a>)
130176
}
177+
178+
private val appWithAttemptHeader = Seq(
179+
"App ID",
180+
"App Name",
181+
"Attempt ID",
182+
"Started",
183+
"Completed",
184+
"Duration",
185+
"Spark User",
186+
"Last Updated")
131187

132188
private def appRow(info: ApplicationHistoryInfo): Seq[Node] = {
133189
val uiAddress = HistoryServer.UI_PATH_PREFIX + s"/${info.id}"
@@ -146,6 +202,69 @@ private[history] class HistoryPage(parent: HistoryServer) extends WebUIPage("")
146202
<td sorttable_customkey={info.lastUpdated.toString}>{lastUpdated}</td>
147203
</tr>
148204
}
205+
206+
private def getAttemptURI(attemptInfo: ApplicationHistoryInfo,
207+
returnEmptyIfAttemptInfoNull: Boolean = true ) = {
208+
if (attemptInfo.appAttemptId.equals("")) {
209+
if(returnEmptyIfAttemptInfoNull) {
210+
attemptInfo.appAttemptId
211+
} else {
212+
HistoryServer.UI_PATH_PREFIX + s"/${attemptInfo.id}"
213+
}
214+
} else {
215+
HistoryServer.UI_PATH_PREFIX + s"/${attemptInfo.id}" + "_" + s"${attemptInfo.appAttemptId}"
216+
}
217+
}
218+
219+
private def firstAttemptRow(attemptInfo : ApplicationHistoryInfo) = {
220+
val uiAddress =
221+
if (attemptInfo.appAttemptId.equals("")) {
222+
attemptInfo.appAttemptId
223+
} else {
224+
HistoryServer.UI_PATH_PREFIX + s"/${attemptInfo.id}" + "_" + s"${attemptInfo.appAttemptId}"
225+
}
226+
227+
val startTime = UIUtils.formatDate(attemptInfo.startTime)
228+
val endTime = UIUtils.formatDate(attemptInfo.endTime)
229+
val duration = UIUtils.formatDuration(attemptInfo.endTime - attemptInfo.startTime)
230+
val lastUpdated = UIUtils.formatDate(attemptInfo.lastUpdated)
231+
val attemptId = attemptInfo.appAttemptId
232+
<td><a href={uiAddress}>{attemptId}</a></td>
233+
<td sorttable_customkey={attemptInfo.startTime.toString}>{startTime}</td>
234+
<td sorttable_customkey={attemptInfo.endTime.toString}>{endTime}</td>
235+
<td sorttable_customkey={(attemptInfo.endTime - attemptInfo.startTime).toString}>
236+
{duration}</td>
237+
<td>{attemptInfo.sparkUser}</td>
238+
<td sorttable_customkey={attemptInfo.lastUpdated.toString}>{lastUpdated}</td>
239+
}
240+
241+
private def attemptRow(attemptInfo: ApplicationHistoryInfo) = {
242+
<tr>
243+
{firstAttemptRow(attemptInfo)}
244+
</tr>
245+
}
246+
247+
private def appWithAttemptRow(
248+
appAttemptsInfo: (String,ArrayBuffer[ApplicationHistoryInfo])): Seq[Node] = {
249+
val applicationId = appAttemptsInfo._1
250+
val info = appAttemptsInfo._2
251+
val rowSpan = info.length
252+
val rowSpanString = rowSpan.toString
253+
val applicatioName = info(0).name
254+
val lastAttemptURI = getAttemptURI(info(0), false)
255+
val ttAttempts = info.slice(1, rowSpan -1)
256+
val x = new xml.NodeBuffer
257+
x +=
258+
<tr>
259+
<td rowspan={rowSpanString}><a href={lastAttemptURI}>{applicationId}</a></td>
260+
<td rowspan={rowSpanString}>{applicatioName}</td>
261+
{ firstAttemptRow(info(0)) }
262+
</tr>;
263+
for( i <- 1 until rowSpan ){
264+
x += attemptRow(info(i))
265+
}
266+
x
267+
}
149268

150269
private def makePageLink(linkPage: Int, showIncomplete: Boolean): String = {
151270
"/?" + Array(

core/src/main/scala/org/apache/spark/scheduler/ApplicationEventListener.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ package org.apache.spark.scheduler
2626
private[spark] class ApplicationEventListener extends SparkListener {
2727
var appName: Option[String] = None
2828
var appId: Option[String] = None
29+
var appAttemptId: Option[String] = None
2930
var sparkUser: Option[String] = None
3031
var startTime: Option[Long] = None
3132
var endTime: Option[Long] = None
@@ -35,6 +36,7 @@ private[spark] class ApplicationEventListener extends SparkListener {
3536
override def onApplicationStart(applicationStart: SparkListenerApplicationStart) {
3637
appName = Some(applicationStart.appName)
3738
appId = applicationStart.appId
39+
appAttemptId = Some(applicationStart.appAttemptId)
3840
startTime = Some(applicationStart.time)
3941
sparkUser = Some(applicationStart.sparkUser)
4042
}

core/src/main/scala/org/apache/spark/scheduler/EventLoggingListener.scala

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,11 @@ private[spark] object EventLoggingListener extends Logging {
266266
appAttemptId: String,
267267
compressionCodecName: Option[String] = None): String = {
268268
val name = appId.replaceAll("[ :/]", "-").replaceAll("[${}'\"]", "_").toLowerCase
269-
270-
if (appAttemptId.equals("")) {
271-
Utils.resolveURI(logBaseDir) + "/" + name.stripSuffix("/")
272-
} else {
273-
Utils.resolveURI(logBaseDir) + "/" + appAttemptId + "/" + name.stripSuffix("/")
274-
}
269+
if (appAttemptId.equals("")) {
270+
logBaseDir.toString.stripSuffix("/") + "/" + name.stripSuffix("/")
271+
} else {
272+
logBaseDir.toString.stripSuffix("/") + "/" + name.stripSuffix("/") + "_" + appAttemptId
273+
}
275274
}
276275

277276
def getLogPath(

core/src/main/scala/org/apache/spark/scheduler/SparkListener.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ case class SparkListenerExecutorMetricsUpdate(
110110
extends SparkListenerEvent
111111

112112
@DeveloperApi
113-
case class SparkListenerApplicationStart(appName: String, appId: Option[String], time: Long,
114-
sparkUser: String) extends SparkListenerEvent
113+
case class SparkListenerApplicationStart(appName: String, appId: Option[String],
114+
time: Long, sparkUser: String, appAttemptId: String = "") extends SparkListenerEvent
115115

116116
@DeveloperApi
117117
case class SparkListenerApplicationEnd(time: Long) extends SparkListenerEvent

core/src/main/scala/org/apache/spark/util/JsonProtocol.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ private[spark] object JsonProtocol {
194194
("App Name" -> applicationStart.appName) ~
195195
("App ID" -> applicationStart.appId.map(JString(_)).getOrElse(JNothing)) ~
196196
("Timestamp" -> applicationStart.time) ~
197-
("User" -> applicationStart.sparkUser)
197+
("User" -> applicationStart.sparkUser) ~
198+
("appAttemptId" -> applicationStart.appAttemptId)
198199
}
199200

200201
def applicationEndToJson(applicationEnd: SparkListenerApplicationEnd): JValue = {
@@ -562,7 +563,8 @@ private[spark] object JsonProtocol {
562563
val appId = Utils.jsonOption(json \ "App ID").map(_.extract[String])
563564
val time = (json \ "Timestamp").extract[Long]
564565
val sparkUser = (json \ "User").extract[String]
565-
SparkListenerApplicationStart(appName, appId, time, sparkUser)
566+
val appAttemptId = (json \ "appAttemptId").extract[String]
567+
SparkListenerApplicationStart(appName, appId, time, sparkUser, appAttemptId)
566568
}
567569

568570
def applicationEndFromJson(json: JValue): SparkListenerApplicationEnd = {

0 commit comments

Comments
 (0)