-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-6707] [CORE][MESOS]: Mesos Scheduler should allow the user to specify constraints based on slave attributes #5563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
92b47fd
Add attributes based constraints support to MesosScheduler
72fe88a
Fix up tests + remove redundant method override, combine utility clas…
ec9d9a6
Add tests for parse constraint string
addedba
Added test case for malformed constraint string
8cc1e8f
Make exception message more explicit about the source of the error
0c64df6
Rename overhead fractions to memory_*, fix spacing
c09ed84
Fixed the access modifier on offerConstraints val to private[mesos]
02031e4
Fix scalastyle warnings in tests
63f53f4
Update codestyle - uniform style for config values
67b58a0
Add documentation for spark.mesos.constraints
fdc0937
Decline offers that did not meet criteria
662535f
Incorporate code review comments + use SparkFunSuite
00be252
Style changes as per code review comments
fc7eb5b
Fix import codestyle
7fee0ea
Add debug statements
c0cbc75
Use offer id value for debug message
1bce782
Fix nit pick whitespace
5ccc32d
Fix nit pick whitespace
482fd71
Update access modifier to private[this] for offer constraints
1a24d0b
Expand scope of attributes matching to include all data types
c3523e7
Added docs
8b73f2d
Fix imports
d83801c
Update code as per code review comments
902535b
Fix line length
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
31 changes: 0 additions & 31 deletions
31
core/src/main/scala/org/apache/spark/scheduler/cluster/mesos/MemoryUtils.scala
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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 hidden or 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 |
|---|---|---|
|
|
@@ -23,14 +23,14 @@ import java.util.{ArrayList => JArrayList, Collections, List => JList} | |
| import scala.collection.JavaConversions._ | ||
| import scala.collection.mutable.{HashMap, HashSet} | ||
|
|
||
| import org.apache.mesos.{Scheduler => MScheduler, _} | ||
| import org.apache.mesos.Protos.{ExecutorInfo => MesosExecutorInfo, TaskInfo => MesosTaskInfo, _} | ||
| import org.apache.mesos.protobuf.ByteString | ||
| import org.apache.mesos.{Scheduler => MScheduler, _} | ||
| import org.apache.spark.{SparkContext, SparkException, TaskState} | ||
| import org.apache.spark.executor.MesosExecutorBackend | ||
| import org.apache.spark.scheduler._ | ||
| import org.apache.spark.scheduler.cluster.ExecutorInfo | ||
| import org.apache.spark.util.Utils | ||
| import org.apache.spark.{SparkContext, SparkException, TaskState} | ||
|
|
||
| /** | ||
| * A SchedulerBackend for running fine-grained tasks on Mesos. Each Spark task is mapped to a | ||
|
|
@@ -59,6 +59,10 @@ private[spark] class MesosSchedulerBackend( | |
|
|
||
| private[mesos] val mesosExecutorCores = sc.conf.getDouble("spark.mesos.mesosExecutor.cores", 1) | ||
|
|
||
| // Offer constraints | ||
| private[this] val slaveOfferConstraints = | ||
| parseConstraintString(sc.conf.get("spark.mesos.constraints", "")) | ||
|
|
||
| @volatile var appId: String = _ | ||
|
|
||
| override def start() { | ||
|
|
@@ -71,8 +75,8 @@ private[spark] class MesosSchedulerBackend( | |
| val executorSparkHome = sc.conf.getOption("spark.mesos.executor.home") | ||
| .orElse(sc.getSparkHome()) // Fall back to driver Spark home for backward compatibility | ||
| .getOrElse { | ||
| throw new SparkException("Executor Spark home `spark.mesos.executor.home` is not set!") | ||
| } | ||
| throw new SparkException("Executor Spark home `spark.mesos.executor.home` is not set!") | ||
| } | ||
| val environment = Environment.newBuilder() | ||
| sc.conf.getOption("spark.executor.extraClassPath").foreach { cp => | ||
| environment.addVariables( | ||
|
|
@@ -115,14 +119,14 @@ private[spark] class MesosSchedulerBackend( | |
| .setName("cpus") | ||
| .setType(Value.Type.SCALAR) | ||
| .setScalar(Value.Scalar.newBuilder() | ||
| .setValue(mesosExecutorCores).build()) | ||
| .setValue(mesosExecutorCores).build()) | ||
| .build() | ||
| val memory = Resource.newBuilder() | ||
| .setName("mem") | ||
| .setType(Value.Type.SCALAR) | ||
| .setScalar( | ||
| Value.Scalar.newBuilder() | ||
| .setValue(MemoryUtils.calculateTotalMemory(sc)).build()) | ||
| .setValue(calculateTotalMemory(sc)).build()) | ||
| .build() | ||
| val executorInfo = MesosExecutorInfo.newBuilder() | ||
| .setExecutorId(ExecutorID.newBuilder().setValue(execId).build()) | ||
|
|
@@ -191,13 +195,31 @@ private[spark] class MesosSchedulerBackend( | |
| val mem = getResource(o.getResourcesList, "mem") | ||
| val cpus = getResource(o.getResourcesList, "cpus") | ||
| val slaveId = o.getSlaveId.getValue | ||
| (mem >= MemoryUtils.calculateTotalMemory(sc) && | ||
| // need at least 1 for executor, 1 for task | ||
| cpus >= (mesosExecutorCores + scheduler.CPUS_PER_TASK)) || | ||
| (slaveIdsWithExecutors.contains(slaveId) && | ||
| cpus >= scheduler.CPUS_PER_TASK) | ||
| val offerAttributes = toAttributeMap(o.getAttributesList) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for rewriting this!! The old code is unbelievably dense. |
||
|
|
||
| // check if all constraints are satisfield | ||
| // 1. Attribute constraints | ||
| // 2. Memory requirements | ||
| // 3. CPU requirements - need at least 1 for executor, 1 for task | ||
| val meetsConstraints = matchesAttributeRequirements(slaveOfferConstraints, offerAttributes) | ||
| val meetsMemoryRequirements = mem >= calculateTotalMemory(sc) | ||
| val meetsCPURequirements = cpus >= (mesosExecutorCores + scheduler.CPUS_PER_TASK) | ||
|
|
||
| val meetsRequirements = | ||
| (meetsConstraints && meetsMemoryRequirements && meetsCPURequirements) || | ||
| (slaveIdsWithExecutors.contains(slaveId) && cpus >= scheduler.CPUS_PER_TASK) | ||
|
|
||
| // add some debug messaging | ||
| val debugstr = if (meetsRequirements) "Accepting" else "Declining" | ||
| val id = o.getId.getValue | ||
| logDebug(s"$debugstr offer: $id with attributes: $offerAttributes mem: $mem cpu: $cpus") | ||
|
|
||
| meetsRequirements | ||
| } | ||
|
|
||
| // Decline offers we ruled out immediately | ||
| unUsableOffers.foreach(o => d.declineOffer(o.getId)) | ||
|
|
||
| val workerOffers = usableOffers.map { o => | ||
| val cpus = if (slaveIdsWithExecutors.contains(o.getSlaveId.getValue)) { | ||
| getResource(o.getResourcesList, "cpus").toInt | ||
|
|
@@ -223,15 +245,15 @@ private[spark] class MesosSchedulerBackend( | |
| val acceptedOffers = scheduler.resourceOffers(workerOffers).filter(!_.isEmpty) | ||
| acceptedOffers | ||
| .foreach { offer => | ||
| offer.foreach { taskDesc => | ||
| val slaveId = taskDesc.executorId | ||
| slaveIdsWithExecutors += slaveId | ||
| slavesIdsOfAcceptedOffers += slaveId | ||
| taskIdToSlaveId(taskDesc.taskId) = slaveId | ||
| mesosTasks.getOrElseUpdate(slaveId, new JArrayList[MesosTaskInfo]) | ||
| .add(createMesosTask(taskDesc, slaveId)) | ||
| } | ||
| offer.foreach { taskDesc => | ||
| val slaveId = taskDesc.executorId | ||
| slaveIdsWithExecutors += slaveId | ||
| slavesIdsOfAcceptedOffers += slaveId | ||
| taskIdToSlaveId(taskDesc.taskId) = slaveId | ||
| mesosTasks.getOrElseUpdate(slaveId, new JArrayList[MesosTaskInfo]) | ||
| .add(createMesosTask(taskDesc, slaveId)) | ||
| } | ||
| } | ||
|
|
||
| // Reply to the offers | ||
| val filters = Filters.newBuilder().setRefuseSeconds(1).build() // TODO: lower timeout? | ||
|
|
@@ -251,8 +273,6 @@ private[spark] class MesosSchedulerBackend( | |
| d.declineOffer(o.getId) | ||
| } | ||
|
|
||
| // Decline offers we ruled out immediately | ||
| unUsableOffers.foreach(o => d.declineOffer(o.getId)) | ||
| } | ||
| } | ||
|
|
||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3 other PRs have made the same change. :) There will likely be some conflict when we start merging them.