|
16 | 16 | */ |
17 | 17 | package org.apache.spark.status.api.v1 |
18 | 18 |
|
19 | | -import javax.ws.rs.{GET, PathParam, Produces} |
| 19 | +import javax.ws.rs._ |
20 | 20 | import javax.ws.rs.core.MediaType |
21 | 21 |
|
22 | 22 | import org.apache.spark.SparkException |
| 23 | +import org.apache.spark.scheduler.StageInfo |
| 24 | +import org.apache.spark.ui.jobs.JobProgressListener |
23 | 25 |
|
24 | 26 | @Produces(Array(MediaType.APPLICATION_JSON)) |
25 | 27 | private[v1] class OneStageResource(uiRoot: UIRoot) { |
26 | 28 |
|
27 | 29 | @GET |
| 30 | + @Path("") |
28 | 31 | def stageData( |
29 | 32 | @PathParam("appId") appId: String, |
30 | 33 | @PathParam("stageId") stageId: Int |
31 | 34 | ): Seq[StageData] = { |
| 35 | + forStage(appId, stageId){ (listener,stageAttempts) => |
| 36 | + stageAttempts.map { case (status, stageInfo) => |
| 37 | + val stageUiData = listener.synchronized { |
| 38 | + listener.stageIdToData.get((stageInfo.stageId, stageInfo.attemptId)). |
| 39 | + getOrElse(throw new SparkException("failed to get full stage data for stage: " + |
| 40 | + stageInfo.stageId + ":" + stageInfo.attemptId) |
| 41 | + ) |
| 42 | + } |
| 43 | + AllStagesResource.stageUiToStageData(status, stageInfo, stageUiData, |
| 44 | + includeDetails = true) |
| 45 | + } |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + @GET |
| 50 | + @Path("/{attemptId: \\d+}") |
| 51 | + def oneAttemptData( |
| 52 | + @PathParam("appId") appId: String, |
| 53 | + @PathParam("stageId") stageId: Int, |
| 54 | + @PathParam("attemptId") attemptId: Int |
| 55 | + ): StageData = { |
| 56 | + forStageAttempt(appId, stageId, attemptId) { case (listener, status, stageInfo) => |
| 57 | + val stageUiData = listener.synchronized { |
| 58 | + listener.stageIdToData.get((stageInfo.stageId, stageInfo.attemptId)). |
| 59 | + getOrElse(throw new SparkException("failed to get full stage data for stage: " + |
| 60 | + stageInfo.stageId + ":" + stageInfo.attemptId) |
| 61 | + ) |
| 62 | + } |
| 63 | + AllStagesResource.stageUiToStageData(status, stageInfo, stageUiData, |
| 64 | + includeDetails = true) |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + @GET |
| 69 | + @Path("/{attemptId: \\d+}/taskSummary") |
| 70 | + def stageData( |
| 71 | + @PathParam("appId") appId: String, |
| 72 | + @PathParam("stageId") stageId: Int, |
| 73 | + @PathParam("attemptId") attemptId: Int, |
| 74 | + @DefaultValue("0.05,0.25,0.5,0.75,0.95") @QueryParam("quantiles") quantileString: String |
| 75 | + ): TaskMetricDistributions = { |
| 76 | + forStageAttempt(appId, stageId, attemptId) { case (listener, status, stageInfo) => |
| 77 | + val stageUiData = listener.synchronized { |
| 78 | + listener.stageIdToData.get((stageInfo.stageId, stageInfo.attemptId)). |
| 79 | + getOrElse(throw new SparkException("failed to get full stage data for stage: " + |
| 80 | + stageInfo.stageId + ":" + stageInfo.attemptId) |
| 81 | + ) |
| 82 | + } |
| 83 | + //TODO error handling |
| 84 | + val quantiles = quantileString.split(",").map{_.toDouble} |
| 85 | + println("quantiles = " + quantiles.mkString(",")) |
| 86 | + AllStagesResource.taskMetricDistributions(stageUiData.taskData.values, quantiles) |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + def forStage[T](appId: String, stageId: Int) |
| 91 | + (f: (JobProgressListener, Seq[(StageStatus, StageInfo)]) => T): T = { |
32 | 92 | uiRoot.withSparkUI(appId) { ui => |
33 | | - val listener = ui.stagesTab.listener |
34 | 93 | val stageAndStatus = AllStagesResource.stagesAndStatus(ui) |
35 | 94 | val stageAttempts = stageAndStatus.flatMap { case (status, stages) => |
36 | | - val matched = stages.filter{ stage => stage.stageId == stageId} |
37 | | - matched.map { status -> _ } |
| 95 | + val matched = stages.filter { stage => stage.stageId == stageId} |
| 96 | + matched.map { |
| 97 | + status -> _ |
| 98 | + } |
38 | 99 | } |
39 | 100 | if (stageAttempts.isEmpty) { |
40 | 101 | throw new NotFoundException("unknown stage: " + stageId) |
41 | 102 | } else { |
42 | | - stageAttempts.map { case (status, stageInfo) => |
43 | | - val stageUiData = listener.synchronized { |
44 | | - listener.stageIdToData.get((stageInfo.stageId, stageInfo.attemptId)). |
45 | | - getOrElse(throw new SparkException("failed to get full stage data for stage: " + |
46 | | - stageInfo.stageId + ":" + stageInfo.attemptId) |
47 | | - ) |
48 | | - } |
49 | | - AllStagesResource.stageUiToStageData(status, stageInfo, stageUiData, |
50 | | - includeDetails = true) |
51 | | - } |
| 103 | + f(ui.jobProgressListener, stageAttempts) |
52 | 104 | } |
| 105 | + } |
| 106 | + } |
53 | 107 |
|
| 108 | + def forStageAttempt[T](appId: String, stageId: Int, attemptId: Int) |
| 109 | + (f: (JobProgressListener, StageStatus, StageInfo) => T): T = { |
| 110 | + forStage(appId, stageId) { case (listener, attempts) => |
| 111 | + val oneAttempt = attempts.filter{ case (status, stage) => |
| 112 | + stage.attemptId == attemptId |
| 113 | + }.headOption |
| 114 | + oneAttempt match { |
| 115 | + case Some((status, stageInfo)) => |
| 116 | + f(listener, status, stageInfo) |
| 117 | + case None => |
| 118 | + val stageAttempts = attempts.map { _._2.attemptId} |
| 119 | + throw new NotFoundException(s"unknown attempt for stage $stageId. " + |
| 120 | + s"Found attempts: ${stageAttempts.mkString("[", ",", "]")}") |
| 121 | + } |
54 | 122 | } |
55 | 123 | } |
56 | 124 | } |
0 commit comments