Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions conf/livy.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
# What spark deploy mode Livy sessions should use.
# livy.spark.deploy-mode =

# What default YARN queue Livy sessions should use if not specified in the client request.
# By default, it is null and falls back to the global Hadoop cluster default queue.
# livy.spark.yarn.queue = default

# Configure Livy server http request and response header size.
# livy.server.request-header.size = 131072
# livy.server.response-header.size = 131072
Expand Down
4 changes: 4 additions & 0 deletions server/src/main/scala/org/apache/livy/LivyConf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ object LivyConf {
val SPARK_HOME = Entry("livy.server.spark-home", null)
val LIVY_SPARK_MASTER = Entry("livy.spark.master", "local")
val LIVY_SPARK_DEPLOY_MODE = Entry("livy.spark.deploy-mode", null)
val SPARK_YARN_QUEUE = Entry("livy.spark.yarn.queue", null)

// Two configurations to specify Spark and related Scala version. These are internal
// configurations will be set by LivyServer and used in session creation. It is not required to
Expand Down Expand Up @@ -485,6 +486,9 @@ class LivyConf(loadDefaults: Boolean) extends ClientConf[LivyConf](null) {
/** Return the spark master Livy sessions should use. */
def sparkMaster(): String = get(LIVY_SPARK_MASTER)

/** Return the default YARN queue Livy sessions should use. */
def sparkYarnQueue(): Option[String] = Option(get(SPARK_YARN_QUEUE)).filterNot(_.isEmpty)

/** Return the path to the spark-submit executable. */
def sparkSubmit(): String = {
sparkHome().map { _ + File.separator + "bin" + File.separator + "spark-submit" }.get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ object BatchSession extends Logging {
request.executorMemory.foreach(builder.executorMemory)
request.executorCores.foreach(builder.executorCores)
request.numExecutors.foreach(builder.numExecutors)
request.queue.foreach(builder.queue)
request.queue.orElse(livyConf.sparkYarnQueue()).foreach(builder.queue)
request.name.foreach(builder.name)

sessionStore.save(BatchSession.RECOVERY_SESSION_TYPE, s.recoveryMetadata)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ object InteractiveSession extends Logging {
SparkLauncher.EXECUTOR_MEMORY -> request.executorMemory.map(_.toString),
"spark.executor.instances" -> request.numExecutors.map(_.toString),
"spark.app.name" -> request.name.map(_.toString),
"spark.yarn.queue" -> request.queue
"spark.yarn.queue" -> request.queue.orElse(livyConf.sparkYarnQueue())
)

userOpts.foreach { case (key, opt) =>
Expand Down Expand Up @@ -152,7 +152,7 @@ object InteractiveSession extends Logging {
request.jars,
request.numExecutors,
request.pyFiles,
request.queue,
request.queue.orElse(livyConf.sparkYarnQueue()),
mockApp)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import org.apache.livy.{LivyBaseUnitTestSuite, LivyConf, Utils}
import org.apache.livy.server.AccessManager
import org.apache.livy.server.recovery.SessionStore
import org.apache.livy.sessions.SessionState
import org.apache.livy.utils.{AppInfo, Clock, SparkApp}
import org.apache.livy.utils.{AppInfo, Clock, SparkApp, SparkProcessBuilder}

class BatchSessionSpec
extends FunSpec
Expand Down Expand Up @@ -138,6 +138,43 @@ class BatchSessionSpec
}) should be (true)
}

it("should pass default YARN queue to spark-submit when request queue is empty") {
val req = new CreateBatchRequest()
req.file = script.toString
req.queue = None
req.conf = Map("spark.driver.extraClassPath" -> sys.props("java.class.path"))

val conf = new LivyConf()
.set(LivyConf.LOCAL_FS_WHITELIST, sys.props("java.io.tmpdir"))
.set(LivyConf.SPARK_YARN_QUEUE, "livy-default-batch-queue")

val accessManager = new AccessManager(conf)
val batch = BatchSession.create(10, None, req, conf, accessManager, null, None, sessionStore)
batch.start()

Utils.waitUntil({ () => !batch.state.isActive }, Duration(10, TimeUnit.SECONDS))
batch.logLines().mkString should include("livy-default-batch-queue")
}

it("should prioritize user-specified request queue over LivyConf configuration") {
val req = new CreateBatchRequest()
req.file = script.toString
req.queue = Some("user-custom-batch-queue")
req.conf = Map("spark.driver.extraClassPath" -> sys.props("java.class.path"))

val conf = new LivyConf()
.set(LivyConf.LOCAL_FS_WHITELIST, sys.props("java.io.tmpdir"))
.set(LivyConf.SPARK_YARN_QUEUE, "livy-default-batch-queue")

val accessManager = new AccessManager(conf)
val batch = BatchSession.create(20, None, req, conf, accessManager, null, None, sessionStore)
batch.start()

Utils.waitUntil({ () => !batch.state.isActive }, Duration(10, TimeUnit.SECONDS))
batch.logLines().mkString should include("user-custom-batch-queue")
batch.logLines().mkString should not include "livy-default-batch-queue"
}

def testRecoverSession(name: Option[String]): Unit = {
val conf = new LivyConf()
val req = new CreateBatchRequest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,4 +322,66 @@ class InteractiveSessionSpec extends FunSpec
s.logLines().mkString should include("RSCDriver URI is unknown")
}
}

describe("InteractiveSession") {
it("should inherit the default YARN queue from LivyConf when request queue is empty") {
val testLivyConf = new LivyConf()
.set(LivyConf.REPL_JARS, "dummy.jar")
.set(LivyConf.SPARK_YARN_QUEUE, "livy-default-queue")

val req = new CreateInteractiveRequest()
req.kind = Spark
req.queue = None
req.conf = Map(RSCConf.Entry.LIVY_JARS.key() -> "")

val mockClient = Some(mock[RSCClient])

val s = InteractiveSession.create(
id = 101,
name = None,
owner = "systest",
proxyUser = None,
livyConf = testLivyConf,
accessManager = accessManager,
request = req,
sessionStore = mock[SessionStore],
ttl = None,
idleTimeout = None,
mockApp = None,
mockClient = mockClient
)

s.queue shouldBe Some("livy-default-queue")
}

it("should prioritize user-specified request queue over LivyConf global configuration") {
val testLivyConf = new LivyConf()
.set(LivyConf.REPL_JARS, "dummy.jar")
.set(LivyConf.SPARK_YARN_QUEUE, "livy-default-queue")

val req = new CreateInteractiveRequest()
req.kind = Spark
req.queue = Some("user-custom-queue")
req.conf = Map(RSCConf.Entry.LIVY_JARS.key() -> "")

val mockClient = Some(mock[RSCClient])

val s = InteractiveSession.create(
id = 102,
name = None,
owner = "systest",
proxyUser = None,
livyConf = testLivyConf,
accessManager = accessManager,
request = req,
sessionStore = mock[SessionStore],
ttl = None,
idleTimeout = None,
mockApp = None,
mockClient = mockClient
)

s.queue shouldBe Some("user-custom-queue")
}
}
}
Loading