-
Notifications
You must be signed in to change notification settings - Fork 28.7k
[SPARK-2147 / 2161] Show removed executors on the UI #1102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fe47402
c89bb6e
fbb65b8
161f8a2
3390b49
792f992
abd72e0
2e2298f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,7 @@ import scala.xml.Node | |
import akka.pattern.ask | ||
import org.json4s.JValue | ||
|
||
import org.apache.spark.deploy.JsonProtocol | ||
import org.apache.spark.deploy.{ExecutorState, JsonProtocol} | ||
import org.apache.spark.deploy.DeployMessages.{MasterStateResponse, RequestMasterState} | ||
import org.apache.spark.deploy.master.ExecutorInfo | ||
import org.apache.spark.ui.{WebUIPage, UIUtils} | ||
|
@@ -57,43 +57,55 @@ private[spark] class ApplicationPage(parent: MasterWebUI) extends WebUIPage("app | |
}) | ||
|
||
val executorHeaders = Seq("ExecutorID", "Worker", "Cores", "Memory", "State", "Logs") | ||
val executors = app.executors.values.toSeq | ||
val executorTable = UIUtils.listingTable(executorHeaders, executorRow, executors) | ||
val allExecutors = (app.executors.values ++ app.removedExecutors).toSet.toSeq | ||
// This includes executors that are either still running or have exited cleanly | ||
val executors = allExecutors.filter { exec => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we could name this something else, now that it needs to be differentiated from removedExecutors / allExecutors. Perhaps also a comment on why we are retaining EXITED executors (unless you have a particularly excellent name in mind). |
||
!ExecutorState.isFinished(exec.state) || exec.state == ExecutorState.EXITED | ||
} | ||
val removedExecutors = allExecutors.diff(executors) | ||
val executorsTable = UIUtils.listingTable(executorHeaders, executorRow, executors) | ||
val removedExecutorsTable = UIUtils.listingTable(executorHeaders, executorRow, removedExecutors) | ||
|
||
val content = | ||
<div class="row-fluid"> | ||
<div class="span12"> | ||
<ul class="unstyled"> | ||
<li><strong>ID:</strong> {app.id}</li> | ||
<li><strong>Name:</strong> {app.desc.name}</li> | ||
<li><strong>User:</strong> {app.desc.user}</li> | ||
<li><strong>Cores:</strong> | ||
{ | ||
if (app.desc.maxCores.isEmpty) { | ||
"Unlimited (%s granted)".format(app.coresGranted) | ||
} else { | ||
"%s (%s granted, %s left)".format( | ||
app.desc.maxCores.get, app.coresGranted, app.coresLeft) | ||
} | ||
} | ||
</li> | ||
<li> | ||
<strong>Executor Memory:</strong> | ||
{Utils.megabytesToString(app.desc.memoryPerSlave)} | ||
</li> | ||
<li><strong>Submit Date:</strong> {app.submitDate}</li> | ||
<li><strong>State:</strong> {app.state}</li> | ||
<li><strong><a href={app.desc.appUiUrl}>Application Detail UI</a></strong></li> | ||
</ul> | ||
</div> | ||
<div class="row-fluid"> | ||
<div class="span12"> | ||
<ul class="unstyled"> | ||
<li><strong>ID:</strong> {app.id}</li> | ||
<li><strong>Name:</strong> {app.desc.name}</li> | ||
<li><strong>User:</strong> {app.desc.user}</li> | ||
<li><strong>Cores:</strong> | ||
{ | ||
if (app.desc.maxCores.isEmpty) { | ||
"Unlimited (%s granted)".format(app.coresGranted) | ||
} else { | ||
"%s (%s granted, %s left)".format( | ||
app.desc.maxCores.get, app.coresGranted, app.coresLeft) | ||
} | ||
} | ||
</li> | ||
<li> | ||
<strong>Executor Memory:</strong> | ||
{Utils.megabytesToString(app.desc.memoryPerSlave)} | ||
</li> | ||
<li><strong>Submit Date:</strong> {app.submitDate}</li> | ||
<li><strong>State:</strong> {app.state}</li> | ||
<li><strong><a href={app.desc.appUiUrl}>Application Detail UI</a></strong></li> | ||
</ul> | ||
</div> | ||
</div> | ||
|
||
<div class="row-fluid"> <!-- Executors --> | ||
<div class="span12"> | ||
<h4> Executor Summary </h4> | ||
{executorTable} | ||
</div> | ||
</div>; | ||
<div class="row-fluid"> <!-- Executors --> | ||
<div class="span12"> | ||
<h4> Executor Summary </h4> | ||
{executorsTable} | ||
{ | ||
if (removedExecutors.nonEmpty) { | ||
<h4> Removed Executors </h4> ++ | ||
removedExecutorsTable | ||
} | ||
} | ||
</div> | ||
</div>; | ||
UIUtils.basicSparkPage(content, "Application: " + app.desc.name) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,74 +46,62 @@ private[spark] class WorkerPage(parent: WorkerWebUI) extends WebUIPage("") { | |
val stateFuture = (workerActor ? RequestWorkerState)(timeout).mapTo[WorkerStateResponse] | ||
val workerState = Await.result(stateFuture, timeout) | ||
|
||
val executorHeaders = Seq("ExecutorID", "Cores", "Memory", "Job Details", "Logs") | ||
val executorHeaders = Seq("ExecutorID", "Cores", "State", "Memory", "Job Details", "Logs") | ||
val runningExecutors = workerState.executors | ||
val runningExecutorTable = | ||
UIUtils.listingTable(executorHeaders, executorRow, workerState.executors) | ||
UIUtils.listingTable(executorHeaders, executorRow, runningExecutors) | ||
val finishedExecutors = workerState.finishedExecutors | ||
val finishedExecutorTable = | ||
UIUtils.listingTable(executorHeaders, executorRow, workerState.finishedExecutors) | ||
UIUtils.listingTable(executorHeaders, executorRow, finishedExecutors) | ||
|
||
val driverHeaders = Seq("DriverID", "Main Class", "State", "Cores", "Memory", "Logs", "Notes") | ||
val runningDrivers = workerState.drivers.sortBy(_.driverId).reverse | ||
val runningDriverTable = UIUtils.listingTable(driverHeaders, driverRow, runningDrivers) | ||
val finishedDrivers = workerState.finishedDrivers.sortBy(_.driverId).reverse | ||
def finishedDriverTable = UIUtils.listingTable(driverHeaders, driverRow, finishedDrivers) | ||
val finishedDriverTable = UIUtils.listingTable(driverHeaders, driverRow, finishedDrivers) | ||
|
||
// For now we only show driver information if the user has submitted drivers to the cluster. | ||
// This is until we integrate the notion of drivers and applications in the UI. | ||
def hasDrivers = runningDrivers.length > 0 || finishedDrivers.length > 0 | ||
|
||
val content = | ||
<div class="row-fluid"> <!-- Worker Details --> | ||
<div class="span12"> | ||
<ul class="unstyled"> | ||
<li><strong>ID:</strong> {workerState.workerId}</li> | ||
<li><strong> | ||
Master URL:</strong> {workerState.masterUrl} | ||
</li> | ||
<li><strong>Cores:</strong> {workerState.cores} ({workerState.coresUsed} Used)</li> | ||
<li><strong>Memory:</strong> {Utils.megabytesToString(workerState.memory)} | ||
({Utils.megabytesToString(workerState.memoryUsed)} Used)</li> | ||
</ul> | ||
<p><a href={workerState.masterWebUiUrl}>Back to Master</a></p> | ||
</div> | ||
<div class="row-fluid"> <!-- Worker Details --> | ||
<div class="span12"> | ||
<ul class="unstyled"> | ||
<li><strong>ID:</strong> {workerState.workerId}</li> | ||
<li><strong> | ||
Master URL:</strong> {workerState.masterUrl} | ||
</li> | ||
<li><strong>Cores:</strong> {workerState.cores} ({workerState.coresUsed} Used)</li> | ||
<li><strong>Memory:</strong> {Utils.megabytesToString(workerState.memory)} | ||
({Utils.megabytesToString(workerState.memoryUsed)} Used)</li> | ||
</ul> | ||
<p><a href={workerState.masterWebUiUrl}>Back to Master</a></p> | ||
</div> | ||
|
||
<div class="row-fluid"> <!-- Running Executors --> | ||
<div class="span12"> | ||
<h4> Running Executors {workerState.executors.size} </h4> | ||
{runningExecutorTable} | ||
</div> | ||
</div> | ||
// scalastyle:off | ||
<div> | ||
{if (hasDrivers) | ||
<div class="row-fluid"> <!-- Running Drivers --> | ||
<div class="span12"> | ||
<h4> Running Drivers {workerState.drivers.size} </h4> | ||
{runningDriverTable} | ||
</div> | ||
</div> | ||
</div> | ||
<div class="row-fluid"> <!-- Executors and Drivers --> | ||
<div class="span12"> | ||
<h4> Running Executors ({runningExecutors.size}) </h4> | ||
{runningExecutorTable} | ||
{ | ||
if (runningDrivers.nonEmpty) { | ||
<h4> Running Drivers ({runningDrivers.size}) </h4> ++ | ||
runningDriverTable | ||
} | ||
} | ||
</div> | ||
|
||
<div class="row-fluid"> <!-- Finished Executors --> | ||
<div class="span12"> | ||
<h4> Finished Executors </h4> | ||
{finishedExecutorTable} | ||
</div> | ||
</div> | ||
|
||
<div> | ||
{if (hasDrivers) | ||
<div class="row-fluid"> <!-- Finished Drivers --> | ||
<div class="span12"> | ||
<h4> Finished Drivers </h4> | ||
{finishedDriverTable} | ||
</div> | ||
</div> | ||
{ | ||
if (finishedExecutors.nonEmpty) { | ||
<h4>Finished Executors ({finishedExecutors.size}) </h4> ++ | ||
finishedExecutorTable | ||
} | ||
} | ||
</div>; | ||
// scalastyle:on | ||
{ | ||
if (finishedDrivers.nonEmpty) { | ||
<h4> Finished Drivers ({finishedDrivers.size}) </h4> ++ | ||
finishedDriverTable | ||
} | ||
} | ||
</div> | ||
</div>; | ||
UIUtils.basicSparkPage(content, "Spark Worker at %s:%s".format( | ||
workerState.host, workerState.port)) | ||
} | ||
|
@@ -122,6 +110,7 @@ private[spark] class WorkerPage(parent: WorkerWebUI) extends WebUIPage("") { | |
<tr> | ||
<td>{executor.execId}</td> | ||
<td>{executor.cores}</td> | ||
<td>{executor.state}</td> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can make this sortable while we're here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it already sortable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, you're right. We only need to specify custom sorting keys. |
||
<td sorttable_customkey={executor.memory.toString}> | ||
{Utils.megabytesToString(executor.memory)} | ||
</td> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mm, if we're going to use ExecutorInfo in a Set, we should at least implement equals() and hashCode() [and toString()?] in case it later actually comes into play. Both can probably just be based on the pre-existing fullId field. Or could make it a case class.