forked from mesos/spark
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Further refactoring, and start of a standalone scheduler backend
- Loading branch information
Showing
13 changed files
with
211 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 0 additions & 10 deletions
10
core/src/main/scala/spark/scheduler/cluster/ClusterSchedulerContext.scala
This file was deleted.
Oops, something went wrong.
15 changes: 15 additions & 0 deletions
15
core/src/main/scala/spark/scheduler/cluster/SchedulerBackend.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package spark.scheduler.cluster | ||
|
||
/** | ||
* A backend interface for cluster scheduling systems that allows plugging in different ones under | ||
* ClusterScheduler. We assume a Mesos-like model where the application gets resource offers as | ||
* machines become available and can launch tasks on them. | ||
*/ | ||
trait SchedulerBackend { | ||
def start(): Unit | ||
def stop(): Unit | ||
def reviveOffers(): Unit | ||
def defaultParallelism(): Int | ||
|
||
// TODO: Probably want to add a killTask too | ||
} |
12 changes: 11 additions & 1 deletion
12
core/src/main/scala/spark/scheduler/cluster/TaskDescription.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,15 @@ | ||
package spark.scheduler.cluster | ||
|
||
import java.nio.channels.Channels | ||
import java.nio.ByteBuffer | ||
import java.io.{IOException, EOFException, ObjectOutputStream, ObjectInputStream} | ||
import spark.util.SerializableByteBuffer | ||
|
||
class TaskDescription(val taskId: Long, val name: String, val serializedTask: ByteBuffer) {} | ||
class TaskDescription(val taskId: Long, val name: String, _serializedTask: ByteBuffer) | ||
extends Serializable { | ||
|
||
// Because ByteBuffers are not serializable, we wrap the task in a SerializableByteBuffer | ||
private val buffer = new SerializableByteBuffer(_serializedTask) | ||
|
||
def serializedTask: ByteBuffer = buffer.value | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
core/src/main/scala/spark/scheduler/standalone/StandaloneClusterMessage.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package spark.scheduler.standalone | ||
|
||
import spark.TaskState.TaskState | ||
import spark.scheduler.cluster.TaskDescription | ||
|
||
sealed trait StandaloneClusterMessage extends Serializable | ||
|
||
case class RegisterSlave(slaveId: String, host: String, cores: Int) extends StandaloneClusterMessage | ||
case class LaunchTask(slaveId: String, task: TaskDescription) extends StandaloneClusterMessage | ||
|
||
case class StatusUpdate(slaveId: String, taskId: Long, state: TaskState, data: Array[Byte]) | ||
extends StandaloneClusterMessage | ||
|
||
case object ReviveOffers extends StandaloneClusterMessage | ||
case object StopMaster extends StandaloneClusterMessage | ||
|
106 changes: 106 additions & 0 deletions
106
core/src/main/scala/spark/scheduler/standalone/StandaloneSchedulerBackend.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package spark.scheduler.standalone | ||
|
||
import scala.collection.mutable.{HashMap, HashSet} | ||
|
||
import akka.actor.{Props, Actor, ActorRef, ActorSystem} | ||
import akka.util.duration._ | ||
import akka.pattern.ask | ||
|
||
import spark.{SparkException, Logging, TaskState} | ||
import spark.TaskState.TaskState | ||
import spark.scheduler.cluster.{WorkerOffer, ClusterScheduler, SchedulerBackend} | ||
import akka.dispatch.Await | ||
import java.nio.ByteBuffer | ||
import java.util.concurrent.atomic.AtomicInteger | ||
|
||
/** | ||
* A standalone scheduler backend, which waits for standalone executors to connect to it through | ||
* Akka. These may be executed in a variety of ways, such as Mesos tasks for the coarse-grained | ||
* Mesos mode or standalone processes for Spark's standalone deploy mode (spark.deploy.*). | ||
*/ | ||
class StandaloneSchedulerBackend(scheduler: ClusterScheduler, actorSystem: ActorSystem) | ||
extends SchedulerBackend | ||
with Logging { | ||
|
||
// Use an atomic variable to track total number of cores in the cluster for simplicity and speed | ||
var totalCoreCount = new AtomicInteger(0) | ||
|
||
class MasterActor extends Actor { | ||
val slaveActor = new HashMap[String, ActorRef] | ||
val slaveHost = new HashMap[String, String] | ||
val freeCores = new HashMap[String, Int] | ||
|
||
def receive = { | ||
case RegisterSlave(slaveId, host, cores) => | ||
slaveActor(slaveId) = sender | ||
logInfo("Registered slave: " + sender + " with ID " + slaveId) | ||
slaveHost(slaveId) = host | ||
freeCores(slaveId) = cores | ||
totalCoreCount.addAndGet(cores) | ||
makeOffers() | ||
|
||
case StatusUpdate(slaveId, taskId, state, data) => | ||
scheduler.statusUpdate(taskId, state, ByteBuffer.wrap(data)) | ||
if (TaskState.isFinished(state)) { | ||
freeCores(slaveId) += 1 | ||
makeOffers(slaveId) | ||
} | ||
|
||
case LaunchTask(slaveId, task) => | ||
freeCores(slaveId) -= 1 | ||
slaveActor(slaveId) ! LaunchTask(slaveId, task) | ||
|
||
case ReviveOffers => | ||
makeOffers() | ||
|
||
case StopMaster => | ||
sender ! true | ||
context.stop(self) | ||
|
||
// TODO: Deal with nodes disconnecting too! (Including decreasing totalCoreCount) | ||
} | ||
|
||
// Make fake resource offers on all slaves | ||
def makeOffers() { | ||
scheduler.resourceOffers( | ||
slaveHost.toArray.map {case (id, host) => new WorkerOffer(id, host, freeCores(id))}) | ||
} | ||
|
||
// Make fake resource offers on just one slave | ||
def makeOffers(slaveId: String) { | ||
scheduler.resourceOffers( | ||
Seq(new WorkerOffer(slaveId, slaveHost(slaveId), freeCores(slaveId)))) | ||
} | ||
} | ||
|
||
var masterActor: ActorRef = null | ||
val taskIdsOnSlave = new HashMap[String, HashSet[String]] | ||
|
||
def start() { | ||
masterActor = actorSystem.actorOf( | ||
Props(new MasterActor), name = StandaloneSchedulerBackend.ACTOR_NAME) | ||
} | ||
|
||
def stop() { | ||
try { | ||
if (masterActor != null) { | ||
val timeout = 5.seconds | ||
val future = masterActor.ask(StopMaster)(timeout) | ||
Await.result(future, timeout) | ||
} | ||
} catch { | ||
case e: Exception => | ||
throw new SparkException("Error stopping standalone scheduler master actor", e) | ||
} | ||
} | ||
|
||
def reviveOffers() { | ||
masterActor ! ReviveOffers | ||
} | ||
|
||
def defaultParallelism(): Int = totalCoreCount.get() | ||
} | ||
|
||
object StandaloneSchedulerBackend { | ||
val ACTOR_NAME = "StandaloneScheduler" | ||
} |
35 changes: 35 additions & 0 deletions
35
core/src/main/scala/spark/util/SerializableByteBuffer.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package spark.util | ||
|
||
import java.nio.ByteBuffer | ||
import java.io.{IOException, ObjectOutputStream, EOFException, ObjectInputStream} | ||
import java.nio.channels.Channels | ||
|
||
/** | ||
* A wrapper around java.nio.ByteBuffer to make it serializable through Java serialization. | ||
*/ | ||
class SerializableByteBuffer(@transient var buffer: ByteBuffer) { | ||
def value = buffer | ||
|
||
private def readObject(in: ObjectInputStream) { | ||
val length = in.readInt() | ||
buffer = ByteBuffer.allocate(length) | ||
var amountRead = 0 | ||
val channel = Channels.newChannel(in) | ||
while (amountRead < length) { | ||
val ret = channel.read(buffer) | ||
if (ret == -1) { | ||
throw new EOFException("End of file before fully reading buffer") | ||
} | ||
amountRead += ret | ||
} | ||
buffer.rewind() // Allow us to read it later | ||
} | ||
|
||
private def writeObject(out: ObjectOutputStream) { | ||
out.writeInt(buffer.limit()) | ||
if (Channels.newChannel(out).write(buffer) != buffer.limit()) { | ||
throw new IOException("Could not fully write buffer to output stream") | ||
} | ||
buffer.rewind() // Allow us to write it again later | ||
} | ||
} |
Oops, something went wrong.