spark.jobserver.LocalContextSupervisorActor.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spark-jobserver Show documentation
Show all versions of spark-jobserver Show documentation
SnappyData distributed data store and execution engine
The newest version!
package spark.jobserver
import akka.actor.{Terminated, Props, ActorRef, PoisonPill}
import akka.pattern.ask
import akka.util.Timeout
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import ooyala.common.akka.InstrumentedActor
import spark.jobserver.JobManagerActor.{SparkContextDead, SparkContextAlive, SparkContextStatus}
import spark.jobserver.io.JobDAO
import spark.jobserver.util.SparkJobUtils
import scala.collection.mutable
import scala.concurrent.Await
import scala.util.{Failure, Success, Try}
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
/** Messages common to all ContextSupervisors */
object ContextSupervisor {
// Messages/actions
case object AddContextsFromConfig // Start up initial contexts
case object ListContexts
case class AddContext(name: String, contextConfig: Config)
case class GetAdHocContext(classPath: String, contextConfig: Config)
case class GetContext(name: String) // returns JobManager, JobResultActor
case class GetResultActor(name: String) // returns JobResultActor
case class StopContext(name: String)
// Errors/Responses
case object ContextInitialized
case class ContextInitError(t: Throwable)
case object ContextAlreadyExists
case object NoSuchContext
case object ContextStopped
}
/**
* This class starts and stops JobManagers / Contexts in-process.
* It is responsible for watching out for the death of contexts/JobManagers.
*
* == Auto context start configuration ==
* Contexts can be configured to be created automatically at job server initialization.
* Configuration example:
* {{{
* spark {
* contexts {
* olap-demo {
* num-cpu-cores = 4 # Number of cores to allocate. Required.
* memory-per-node = 1024m # Executor memory per node, -Xmx style eg 512m, 1G, etc.
* }
* }
* }
* }}}
*
* == Other configuration ==
* {{{
* spark {
* jobserver {
* context-creation-timeout = 15 s
* yarn-context-creation-timeout = 40 s
* }
*
* # Default settings for all context creation
* context-settings {
* spark.mesos.coarse = true
* }
* }
* }}}
*/
class LocalContextSupervisorActor(dao: JobDAO) extends InstrumentedActor {
import ContextSupervisor._
import scala.collection.JavaConverters._
import scala.concurrent.duration._
val config = context.system.settings.config
val defaultContextConfig = config.getConfig("spark.context-settings")
val contextTimeout = SparkJobUtils.getContextTimeout(config)
import context.dispatcher // to get ExecutionContext for futures
private val contexts = mutable.HashMap.empty[String, ActorRef]
private val resultActors = mutable.HashMap.empty[String, ActorRef]
// This is for capturing results for ad-hoc jobs. Otherwise when ad-hoc job dies, resultActor also dies,
// and there is no way to retrieve results.
val globalResultActor = context.actorOf(Props[JobResultActor], "global-result-actor")
def wrappedReceive: Receive = {
case AddContextsFromConfig =>
addContextsFromConfig(config)
case ListContexts =>
sender ! contexts.keys.toSeq
case AddContext(name, contextConfig) =>
val originator = sender // Sender is a mutable reference, must capture in immutable val
val mergedConfig = contextConfig.withFallback(defaultContextConfig)
if (contexts contains name) {
originator ! ContextAlreadyExists
} else {
startContext(name, mergedConfig, false, contextTimeout) { contextMgr =>
originator ! ContextInitialized
} { err =>
originator ! ContextInitError(err)
}
}
case GetAdHocContext(classPath, contextConfig) =>
val originator = sender // Sender is a mutable reference, must capture in immutable val
logger.info("Creating SparkContext for adhoc jobs.")
val mergedConfig = contextConfig.withFallback(defaultContextConfig)
// Keep generating context name till there is no collision
var contextName = ""
do {
contextName = java.util.UUID.randomUUID().toString().substring(0, 8) + "-" + classPath
} while (contexts contains contextName)
// Create JobManagerActor and JobResultActor
startContext(contextName, mergedConfig, true, contextTimeout) { contextMgr =>
originator ! (contexts(contextName), resultActors(contextName))
} { err =>
originator ! ContextInitError(err)
}
case GetResultActor(name) =>
sender ! resultActors.get(name).getOrElse(globalResultActor)
case GetContext(name) =>
if (contexts contains name) {
val future = (contexts(name) ? SparkContextStatus) (contextTimeout.seconds)
val originator = sender
future.collect {
case SparkContextAlive => originator ! (contexts(name), resultActors(name))
case SparkContextDead =>
logger.info("SparkContext {} is dead", name)
self ! StopContext(name)
originator ! NoSuchContext
}
} else {
sender ! NoSuchContext
}
case StopContext(name) =>
if (contexts contains name) {
logger.info("Shutting down context {}", name)
context.watch(contexts(name))
contexts(name) ! PoisonPill
resultActors.remove(name)
sender ! ContextStopped
} else {
sender ! NoSuchContext
}
case Terminated(actorRef) =>
val name :String = actorRef.path.name
logger.info("Actor terminated: " + name)
contexts.remove(name)
}
private def startContext(name: String, contextConfig: Config, isAdHoc: Boolean, timeoutSecs: Int = 1)
(successFunc: ActorRef => Unit)
(failureFunc: Throwable => Unit) {
require(!(contexts contains name), "There is already a context named " + name)
logger.info("Creating a SparkContext named {}", name)
val resultActorRef = if (isAdHoc) Some(globalResultActor) else None
val ref = context.actorOf(Props(
classOf[JobManagerActor], dao, name, contextConfig, isAdHoc, resultActorRef), name)
(ref ? JobManagerActor.Initialize)(Timeout(timeoutSecs.second)).onComplete {
case Failure(e: Exception) =>
logger.error("Exception after sending Initialize to JobManagerActor", e)
// Make sure we try to shut down the context in case it gets created anyways
ref ! PoisonPill
failureFunc(e)
case Success(JobManagerActor.Initialized(resultActor)) =>
logger.info("SparkContext {} initialized", name)
contexts(name) = ref
resultActors(name) = resultActor
successFunc(ref)
case Success(JobManagerActor.InitError(t)) =>
ref ! PoisonPill
failureFunc(t)
case x =>
logger.warn("Unexpected message received by startContext: {}", x)
}
}
// Adds the contexts from the config file
private def addContextsFromConfig(config: Config) {
for (contexts <- Try(config.getObject("spark.contexts"))) {
contexts.keySet().asScala.foreach { contextName =>
val contextConfig = config.getConfig("spark.contexts." + contextName)
.withFallback(defaultContextConfig)
startContext(contextName, contextConfig, false, contextTimeout) { ref => } {
e => logger.error("Unable to start context " + contextName, e)
}
Thread sleep 500 // Give some spacing so multiple contexts can be created
}
}
}
}