[WIP]: [Livy-336]: Livy should not spawn one thread per job to track the job on Yarn (Use YARN REST API)#44
[WIP]: [Livy-336]: Livy should not spawn one thread per job to track the job on Yarn (Use YARN REST API)#44meisam wants to merge 2 commits into
Conversation
Yarn * Moved YARN related code from `SparkYarnApp` to a new class `YarnInterface` * Removed "yarnAppMinotorThreads" that are created for each Spark application * Created one thread that polls YARN to get `ApplicationId`s and * Created a pool of threads that gets `ApplicationAttempts` and `ContainerReports` from YARN * because there is no API to get `ApplicationAttempts` and `ContainerReports` in bulk * the thread pool is actually used by Scala `Future`s * Updated test cases in `SparkYarnAppSpec` to match the new design Task-Url: https://issues.cloudera.org/browse/LIVY-336
YARN interface useses REST API now.
Not sure that brings a lot of gains; unless something changed, Livy uses a bunch of HDFS APIs already, so removing YARN would probably not remove a whole lot of dependencies. |
On top of that, if we remove all dependencies to YARN, we have to put the YARN cofings, such as resouce manager REST URL, in |
Codecov Report
@@ Coverage Diff @@
## master #44 +/- ##
============================================
- Coverage 70.73% 66.52% -4.21%
+ Complexity 788 787 -1
============================================
Files 97 99 +2
Lines 5388 5556 +168
Branches 802 850 +48
============================================
- Hits 3811 3696 -115
- Misses 1037 1323 +286
+ Partials 540 537 -3
Continue to review full report at Codecov.
|
|
What's the benefit of using REST API? |
|
The main benefit is that with one call we can get all the info we need for the app, that is appId, state, finalStatus, diagnostics, trackingUrl, SparkUiUrl, etc. There is one other potential benefit, which is we can drop dependencies to YARN. But, as Marcelo described, the gains from dropping YARN dependencies are minimal. I am alright with either YARN's Java API or REST API. I just added this PR because the proposal on the mailing list recommended REST. |
ajbozarth
left a comment
There was a problem hiding this comment.
Gave feedback where I found it, but I'm no YARN expect so my review of the new YarnInterface class wasn't detailed. I also haven't had a chance to read through the Spec changes.
| val YARN_REST_HOST = Entry("livy.server.yarn.rest.host", "localhost") | ||
|
|
||
| // The resource manager port for YARN's REST API. | ||
| val YARN_REST_PORT = Entry("livy.server.yarn.rest.port", 0) |
There was a problem hiding this comment.
I'm pretty sure a default port of 0 is a bad idea here
There was a problem hiding this comment.
I'll change it the the default port 8088
| // The resource manager port for YARN's REST API. | ||
| val YARN_REST_PORT = Entry("livy.server.yarn.rest.port", 0) | ||
|
|
||
| // The resource manager port for YARN's REST API. |
| val YARN_REST_PORT = Entry("livy.server.yarn.rest.port", 0) | ||
|
|
||
| // The resource manager port for YARN's REST API. | ||
| val YARN_REST_PATH = Entry("livy.server.yarn.rest.path", "/ws/v1/cluster/apps") |
There was a problem hiding this comment.
why /ws/v1/cluster/apps? I'm not sure of the purpose of that particular path
There was a problem hiding this comment.
It is just the default path. See https://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-site/ResourceManagerRest.html#Cluster_Information_API
| state: String, | ||
| trackingUI: Option[String], | ||
| trackingUrl: Option[String] | ||
| ) |
There was a problem hiding this comment.
Two issues with this whole declaration:
- Lots of commented out params, why?
- I'm pretty sure this is bad style, try running style check and looking at other similar declarations
There was a problem hiding this comment.
This is one thing that I wanted to discuss with the Livy community. Scala cannot have case classes with more than 22 fields. I put all the fields that we can get from YARN REST for reference, but I commented out ones that are not as useful for Livy. I'd like to get feedback from the Livy users on which fields we should keep and which one we should leave out.
There was a problem hiding this comment.
That makes sense, @jerryshao @zjffdu you guys probably know this side of Livy better, what fields do you think are best to include?
There was a problem hiding this comment.
@meisam I thought that only applied to case classes in 2.10; so you could work around it by not having a case class or by using 2.11 (which is what Livy uses, no?).
There was a problem hiding this comment.
Livy itself is still built with scala 2.10, there's an open JIRA for the change that I tried to get traction on a while back with no response.
| SparkApp.State.KILLED | ||
| case (state, finalStatus) => // any other combination is invalid | ||
| error(s"Unknown YARN state $state for app $appId with final status $finalStatus.") | ||
| SparkApp.State.FAILED |
There was a problem hiding this comment.
Are the above changes just #39 pulled over so this change works?
There was a problem hiding this comment.
Yes, except for one thing. #39 uses Java objects and enums. With YARN REST API, these are string values.
There was a problem hiding this comment.
Ok so this is a separate but related/dependent change then?
There was a problem hiding this comment.
That is right. This is a separate but related change.
| import SparkYarnApp._ | ||
|
|
||
| private val appIdPromise: Promise[ApplicationId] = Promise() | ||
| import scala.concurrent.ExecutionContext.Implicits.global |
There was a problem hiding this comment.
What is the purpose of this import?
There was a problem hiding this comment.
It is needed as an implicit parameter to scala Future.
There was a problem hiding this comment.
It is? I've never noticed it in other classes that use scala Future
| import scala.concurrent.ExecutionContext.Implicits.global | ||
|
|
||
| val appId: Future[ApplicationId] = Future { | ||
| appIdOption.fold(yarnInterface.getAppIdFromTag(appTag, process))(ConverterUtils.toApplicationId) |
There was a problem hiding this comment.
This seems messy but personally I'm not sure how exactly to clean it up
There was a problem hiding this comment.
I took multiple shots at this, and this is the best that I could come up with. More feedback is appreciated.
There was a problem hiding this comment.
I'll trust you on that and say this is ok then
| class YarnInterface(livyConf: LivyConf, yarnClient: YarnClient, httpClient: HttpClient) | ||
| extends Logging { | ||
|
|
||
| import scala.concurrent.ExecutionContext.Implicits.global |
| // | ||
| // def error(s: String) = { | ||
| // println(s) | ||
| // } |
There was a problem hiding this comment.
these debugs should be removed
| val limit = livyConf.getInt(LivyConf.YARN_REST_REPORTS_LIMIT) | ||
| val query = s"applicationTypes=$applicationTypes&limit=$limit" | ||
|
|
||
| // parameters URI(scheme, user, host, port, path, query, fragment) |
There was a problem hiding this comment.
Just to clarify null parameters on the next line. The URI class has too many constructors.
There was a problem hiding this comment.
I think it's ok without, the URI class is a pretty standard class
|
Thanks @ajbozarth. This was helpful feedback. |
|
I don't have a strong preference on changing to REST APIs, except one advantage you mentioned above (get all infos in one request). I'm not sure usually how many application will we launch in one Livy server, if it is only tens or hundreds of application, I think the communication overhead should not be big. Also avoiding yarn dependency is not a big problem, anyway we still need to depend on hadoop jars. One difference I can think of is to visit security Hadoop, now because we change to REST API, so we should provide spnego principal/keytab, rather than Livy principal/keytab. |
The main gain from this PR is LIVY-336: Livy should not spawn one thread per job to track the job on Yarn.
YARN's Cluster Applications API does not need authentication with one exception: deleting a YARN app needs authentication. But the delete API is experimental and I am not sure if it is a good idea to use it. Since dropping the YARN dependencies has minimal benefits, it makes sense to kill YARN apps with java |
|
This pull request has been automatically marked as stale because it has had no activity for at least 3 months. If you are still working on this change or plan to move it forward, please leave a comment or push a new commit so we know to keep it open. Otherwise, this PR will be closed automatically in about one month. Thank you for your contribution to Apache Livy! |
|
Closing this pull request due to at least 4 months of inactivity. If you would like to continue the work, please feel free to reopen this pull request or open a new one. |
What changes were proposed in this pull request?
Currently, Livy spawns one thread to for each Spark application to track the status of the application on YARN. The threads are called
yarnAppMonitorThread-xxxxand make multiple calls to YARN using YARN's Java API to get the status of running applications or to kill applications. Spawning so many threads is a bottle neck and limits the number of Spark applications that can be submitted in a short span of time.This pull request addresses the above issue in 2 ways:
How was this patch tested?
What is not in the scope of this pull request?
Based on the discussion and feedback on this pull request, and based on the feedback
yarnClient.killwont be replaced with REST API to kill YARN apps because:Task-url: https://issues.apache.org/jira/browse/LIVY-336