Skip to content

Commit 7265865

Browse files
author
Andrew Or
committed
Merge branch 'master' of github.com:apache/spark into clean-more
Conflicts: core/src/main/scala/org/apache/spark/util/ClosureCleaner.scala
2 parents df3caa3 + 49549d5 commit 7265865

File tree

75 files changed

+3160
-473
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+3160
-473
lines changed

.rat-excludes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ TAGS
1515
RELEASE
1616
control
1717
docs
18+
docker.properties.template
1819
fairscheduler.xml.template
1920
spark-defaults.conf.template
2021
log4j.properties

conf/docker.properties.template

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
spark.mesos.executor.docker.image: <image built from `../docker/spark-mesos/Dockerfile`>
2+
spark.mesos.executor.docker.volumes: /usr/local/lib:/host/usr/local/lib:ro
3+
spark.mesos.executor.home: /opt/spark

core/src/main/scala/org/apache/spark/Aggregator.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,7 @@ case class Aggregator[K, V, C] (
8888
combiners.iterator
8989
} else {
9090
val combiners = new ExternalAppendOnlyMap[K, C, C](identity, mergeCombiners, mergeCombiners)
91-
while (iter.hasNext) {
92-
val pair = iter.next()
93-
combiners.insert(pair._1, pair._2)
94-
}
91+
combiners.insertAll(iter)
9592
// Update task metrics if context is not null
9693
// TODO: Make context non-optional in a future release
9794
Option(context).foreach { c =>

core/src/main/scala/org/apache/spark/ExecutorAllocationManager.scala

Lines changed: 49 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,20 @@ import org.apache.spark.util.{ThreadUtils, Clock, SystemClock, Utils}
2727
/**
2828
* An agent that dynamically allocates and removes executors based on the workload.
2929
*
30-
* The add policy depends on whether there are backlogged tasks waiting to be scheduled. If
31-
* the scheduler queue is not drained in N seconds, then new executors are added. If the queue
32-
* persists for another M seconds, then more executors are added and so on. The number added
33-
* in each round increases exponentially from the previous round until an upper bound on the
34-
* number of executors has been reached. The upper bound is based both on a configured property
35-
* and on the number of tasks pending: the policy will never increase the number of executor
36-
* requests past the number needed to handle all pending tasks.
30+
* The ExecutorAllocationManager maintains a moving target number of executors which is periodically
31+
* synced to the cluster manager. The target starts at a configured initial value and changes with
32+
* the number of pending and running tasks.
33+
*
34+
* Decreasing the target number of executors happens when the current target is more than needed to
35+
* handle the current load. The target number of executors is always truncated to the number of
36+
* executors that could run all current running and pending tasks at once.
37+
*
38+
* Increasing the target number of executors happens in response to backlogged tasks waiting to be
39+
* scheduled. If the scheduler queue is not drained in N seconds, then new executors are added. If
40+
* the queue persists for another M seconds, then more executors are added and so on. The number
41+
* added in each round increases exponentially from the previous round until an upper bound has been
42+
* reached. The upper bound is based both on a configured property and on the current number of
43+
* running and pending tasks, as described above.
3744
*
3845
* The rationale for the exponential increase is twofold: (1) Executors should be added slowly
3946
* in the beginning in case the number of extra executors needed turns out to be small. Otherwise,
@@ -105,8 +112,10 @@ private[spark] class ExecutorAllocationManager(
105112
// Number of executors to add in the next round
106113
private var numExecutorsToAdd = 1
107114

108-
// Number of executors that have been requested but have not registered yet
109-
private var numExecutorsPending = 0
115+
// The desired number of executors at this moment in time. If all our executors were to die, this
116+
// is the number of executors we would immediately want from the cluster manager.
117+
private var numExecutorsTarget =
118+
conf.getInt("spark.dynamicAllocation.initialExecutors", minNumExecutors)
110119

111120
// Executors that have been requested to be removed but have not been killed yet
112121
private val executorsPendingToRemove = new mutable.HashSet[String]
@@ -199,13 +208,6 @@ private[spark] class ExecutorAllocationManager(
199208
executor.awaitTermination(10, TimeUnit.SECONDS)
200209
}
201210

202-
/**
203-
* The number of executors we would have if the cluster manager were to fulfill all our existing
204-
* requests.
205-
*/
206-
private def targetNumExecutors(): Int =
207-
numExecutorsPending + executorIds.size - executorsPendingToRemove.size
208-
209211
/**
210212
* The maximum number of executors we would need under the current load to satisfy all running
211213
* and pending tasks, rounded up.
@@ -227,7 +229,7 @@ private[spark] class ExecutorAllocationManager(
227229
private def schedule(): Unit = synchronized {
228230
val now = clock.getTimeMillis
229231

230-
addOrCancelExecutorRequests(now)
232+
updateAndSyncNumExecutorsTarget(now)
231233

232234
removeTimes.retain { case (executorId, expireTime) =>
233235
val expired = now >= expireTime
@@ -239,26 +241,28 @@ private[spark] class ExecutorAllocationManager(
239241
}
240242

241243
/**
244+
* Updates our target number of executors and syncs the result with the cluster manager.
245+
*
242246
* Check to see whether our existing allocation and the requests we've made previously exceed our
243-
* current needs. If so, let the cluster manager know so that it can cancel pending requests that
244-
* are unneeded.
247+
* current needs. If so, truncate our target and let the cluster manager know so that it can
248+
* cancel pending requests that are unneeded.
245249
*
246250
* If not, and the add time has expired, see if we can request new executors and refresh the add
247251
* time.
248252
*
249253
* @return the delta in the target number of executors.
250254
*/
251-
private def addOrCancelExecutorRequests(now: Long): Int = synchronized {
252-
val currentTarget = targetNumExecutors
255+
private def updateAndSyncNumExecutorsTarget(now: Long): Int = synchronized {
253256
val maxNeeded = maxNumExecutorsNeeded
254257

255-
if (maxNeeded < currentTarget) {
258+
if (maxNeeded < numExecutorsTarget) {
256259
// The target number exceeds the number we actually need, so stop adding new
257-
// executors and inform the cluster manager to cancel the extra pending requests.
258-
val newTotalExecutors = math.max(maxNeeded, minNumExecutors)
259-
client.requestTotalExecutors(newTotalExecutors)
260+
// executors and inform the cluster manager to cancel the extra pending requests
261+
val oldNumExecutorsTarget = numExecutorsTarget
262+
numExecutorsTarget = math.max(maxNeeded, minNumExecutors)
263+
client.requestTotalExecutors(numExecutorsTarget)
260264
numExecutorsToAdd = 1
261-
updateNumExecutorsPending(newTotalExecutors)
265+
numExecutorsTarget - oldNumExecutorsTarget
262266
} else if (addTime != NOT_SET && now >= addTime) {
263267
val delta = addExecutors(maxNeeded)
264268
logDebug(s"Starting timer to add more executors (to " +
@@ -281,21 +285,30 @@ private[spark] class ExecutorAllocationManager(
281285
*/
282286
private def addExecutors(maxNumExecutorsNeeded: Int): Int = {
283287
// Do not request more executors if it would put our target over the upper bound
284-
val currentTarget = targetNumExecutors
285-
if (currentTarget >= maxNumExecutors) {
286-
logDebug(s"Not adding executors because there are already ${executorIds.size} " +
287-
s"registered and $numExecutorsPending pending executor(s) (limit $maxNumExecutors)")
288+
if (numExecutorsTarget >= maxNumExecutors) {
289+
val numExecutorsPending = numExecutorsTarget - executorIds.size
290+
logDebug(s"Not adding executors because there are already ${executorIds.size} registered " +
291+
s"and ${numExecutorsPending} pending executor(s) (limit $maxNumExecutors)")
288292
numExecutorsToAdd = 1
289293
return 0
290294
}
291295

292-
val actualMaxNumExecutors = math.min(maxNumExecutors, maxNumExecutorsNeeded)
293-
val newTotalExecutors = math.min(currentTarget + numExecutorsToAdd, actualMaxNumExecutors)
294-
val addRequestAcknowledged = testing || client.requestTotalExecutors(newTotalExecutors)
296+
val oldNumExecutorsTarget = numExecutorsTarget
297+
// There's no point in wasting time ramping up to the number of executors we already have, so
298+
// make sure our target is at least as much as our current allocation:
299+
numExecutorsTarget = math.max(numExecutorsTarget, executorIds.size)
300+
// Boost our target with the number to add for this round:
301+
numExecutorsTarget += numExecutorsToAdd
302+
// Ensure that our target doesn't exceed what we need at the present moment:
303+
numExecutorsTarget = math.min(numExecutorsTarget, maxNumExecutorsNeeded)
304+
// Ensure that our target fits within configured bounds:
305+
numExecutorsTarget = math.max(math.min(numExecutorsTarget, maxNumExecutors), minNumExecutors)
306+
307+
val addRequestAcknowledged = testing || client.requestTotalExecutors(numExecutorsTarget)
295308
if (addRequestAcknowledged) {
296-
val delta = updateNumExecutorsPending(newTotalExecutors)
309+
val delta = numExecutorsTarget - oldNumExecutorsTarget
297310
logInfo(s"Requesting $delta new executor(s) because tasks are backlogged" +
298-
s" (new desired total will be $newTotalExecutors)")
311+
s" (new desired total will be $numExecutorsTarget)")
299312
numExecutorsToAdd = if (delta == numExecutorsToAdd) {
300313
numExecutorsToAdd * 2
301314
} else {
@@ -304,23 +317,11 @@ private[spark] class ExecutorAllocationManager(
304317
delta
305318
} else {
306319
logWarning(
307-
s"Unable to reach the cluster manager to request $newTotalExecutors total executors!")
320+
s"Unable to reach the cluster manager to request $numExecutorsTarget total executors!")
308321
0
309322
}
310323
}
311324

312-
/**
313-
* Given the new target number of executors, update the number of pending executor requests,
314-
* and return the delta from the old number of pending requests.
315-
*/
316-
private def updateNumExecutorsPending(newTotalExecutors: Int): Int = {
317-
val newNumExecutorsPending =
318-
newTotalExecutors - executorIds.size + executorsPendingToRemove.size
319-
val delta = newNumExecutorsPending - numExecutorsPending
320-
numExecutorsPending = newNumExecutorsPending
321-
delta
322-
}
323-
324325
/**
325326
* Request the cluster manager to remove the given executor.
326327
* Return whether the request is received.
@@ -372,10 +373,6 @@ private[spark] class ExecutorAllocationManager(
372373
// as idle again so as not to forget that it is a candidate for removal. (see SPARK-4951)
373374
executorIds.filter(listener.isExecutorIdle).foreach(onExecutorIdle)
374375
logInfo(s"New executor $executorId has registered (new total is ${executorIds.size})")
375-
if (numExecutorsPending > 0) {
376-
numExecutorsPending -= 1
377-
logDebug(s"Decremented number of pending executors ($numExecutorsPending left)")
378-
}
379376
} else {
380377
logWarning(s"Duplicate executor $executorId has registered")
381378
}

core/src/main/scala/org/apache/spark/SecurityManager.scala

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,13 @@ import org.apache.spark.util.Utils
150150
* authorization. If not filter is in place the user is generally null and no authorization
151151
* can take place.
152152
*
153-
* Connection encryption (SSL) configuration is organized hierarchically. The user can configure
154-
* the default SSL settings which will be used for all the supported communication protocols unless
153+
* When authentication is being used, encryption can also be enabled by setting the option
154+
* spark.authenticate.enableSaslEncryption to true. This is only supported by communication
155+
* channels that use the network-common library, and can be used as an alternative to SSL in those
156+
* cases.
157+
*
158+
* SSL can be used for encryption for certain communication channels. The user can configure the
159+
* default SSL settings which will be used for all the supported communication protocols unless
155160
* they are overwritten by protocol specific settings. This way the user can easily provide the
156161
* common settings for all the protocols without disabling the ability to configure each one
157162
* individually.
@@ -412,6 +417,14 @@ private[spark] class SecurityManager(sparkConf: SparkConf)
412417
*/
413418
def isAuthenticationEnabled(): Boolean = authOn
414419

420+
/**
421+
* Checks whether SASL encryption should be enabled.
422+
* @return Whether to enable SASL encryption when connecting to services that support it.
423+
*/
424+
def isSaslEncryptionEnabled(): Boolean = {
425+
sparkConf.getBoolean("spark.authenticate.enableSaslEncryption", false)
426+
}
427+
415428
/**
416429
* Gets the user used for authenticating HTTP connections.
417430
* For now use a single hardcoded user.

core/src/main/scala/org/apache/spark/deploy/Client.scala

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.spark.deploy
1919

20+
import scala.collection.mutable.HashSet
2021
import scala.concurrent._
2122

2223
import akka.actor._
@@ -31,21 +32,24 @@ import org.apache.spark.util.{ActorLogReceive, AkkaUtils, RpcUtils, Utils}
3132

3233
/**
3334
* Proxy that relays messages to the driver.
35+
*
36+
* We currently don't support retry if submission fails. In HA mode, client will submit request to
37+
* all masters and see which one could handle it.
3438
*/
3539
private class ClientActor(driverArgs: ClientArguments, conf: SparkConf)
3640
extends Actor with ActorLogReceive with Logging {
3741

38-
var masterActor: ActorSelection = _
42+
private val masterActors = driverArgs.masters.map { m =>
43+
context.actorSelection(Master.toAkkaUrl(m, AkkaUtils.protocol(context.system)))
44+
}
45+
private val lostMasters = new HashSet[Address]
46+
private var activeMasterActor: ActorSelection = null
47+
3948
val timeout = RpcUtils.askTimeout(conf)
4049

4150
override def preStart(): Unit = {
42-
masterActor = context.actorSelection(
43-
Master.toAkkaUrl(driverArgs.master, AkkaUtils.protocol(context.system)))
44-
4551
context.system.eventStream.subscribe(self, classOf[RemotingLifecycleEvent])
4652

47-
println(s"Sending ${driverArgs.cmd} command to ${driverArgs.master}")
48-
4953
driverArgs.cmd match {
5054
case "launch" =>
5155
// TODO: We could add an env variable here and intercept it in `sc.addJar` that would
@@ -79,11 +83,17 @@ private class ClientActor(driverArgs: ClientArguments, conf: SparkConf)
7983
driverArgs.supervise,
8084
command)
8185

82-
masterActor ! RequestSubmitDriver(driverDescription)
86+
// This assumes only one Master is active at a time
87+
for (masterActor <- masterActors) {
88+
masterActor ! RequestSubmitDriver(driverDescription)
89+
}
8390

8491
case "kill" =>
8592
val driverId = driverArgs.driverId
86-
masterActor ! RequestKillDriver(driverId)
93+
// This assumes only one Master is active at a time
94+
for (masterActor <- masterActors) {
95+
masterActor ! RequestKillDriver(driverId)
96+
}
8797
}
8898
}
8999

@@ -92,10 +102,9 @@ private class ClientActor(driverArgs: ClientArguments, conf: SparkConf)
92102
println("... waiting before polling master for driver state")
93103
Thread.sleep(5000)
94104
println("... polling master for driver state")
95-
val statusFuture = (masterActor ? RequestDriverStatus(driverId))(timeout)
105+
val statusFuture = (activeMasterActor ? RequestDriverStatus(driverId))(timeout)
96106
.mapTo[DriverStatusResponse]
97107
val statusResponse = Await.result(statusFuture, timeout)
98-
99108
statusResponse.found match {
100109
case false =>
101110
println(s"ERROR: Cluster master did not recognize $driverId")
@@ -122,20 +131,46 @@ private class ClientActor(driverArgs: ClientArguments, conf: SparkConf)
122131

123132
case SubmitDriverResponse(success, driverId, message) =>
124133
println(message)
125-
if (success) pollAndReportStatus(driverId.get) else System.exit(-1)
134+
if (success) {
135+
activeMasterActor = context.actorSelection(sender.path)
136+
pollAndReportStatus(driverId.get)
137+
} else if (!Utils.responseFromBackup(message)) {
138+
System.exit(-1)
139+
}
140+
126141

127142
case KillDriverResponse(driverId, success, message) =>
128143
println(message)
129-
if (success) pollAndReportStatus(driverId) else System.exit(-1)
144+
if (success) {
145+
activeMasterActor = context.actorSelection(sender.path)
146+
pollAndReportStatus(driverId)
147+
} else if (!Utils.responseFromBackup(message)) {
148+
System.exit(-1)
149+
}
130150

131151
case DisassociatedEvent(_, remoteAddress, _) =>
132-
println(s"Error connecting to master ${driverArgs.master} ($remoteAddress), exiting.")
133-
System.exit(-1)
152+
if (!lostMasters.contains(remoteAddress)) {
153+
println(s"Error connecting to master $remoteAddress.")
154+
lostMasters += remoteAddress
155+
// Note that this heuristic does not account for the fact that a Master can recover within
156+
// the lifetime of this client. Thus, once a Master is lost it is lost to us forever. This
157+
// is not currently a concern, however, because this client does not retry submissions.
158+
if (lostMasters.size >= masterActors.size) {
159+
println("No master is available, exiting.")
160+
System.exit(-1)
161+
}
162+
}
134163

135164
case AssociationErrorEvent(cause, _, remoteAddress, _, _) =>
136-
println(s"Error connecting to master ${driverArgs.master} ($remoteAddress), exiting.")
137-
println(s"Cause was: $cause")
138-
System.exit(-1)
165+
if (!lostMasters.contains(remoteAddress)) {
166+
println(s"Error connecting to master ($remoteAddress).")
167+
println(s"Cause was: $cause")
168+
lostMasters += remoteAddress
169+
if (lostMasters.size >= masterActors.size) {
170+
println("No master is available, exiting.")
171+
System.exit(-1)
172+
}
173+
}
139174
}
140175
}
141176

@@ -163,7 +198,9 @@ object Client {
163198
"driverClient", Utils.localHostName(), 0, conf, new SecurityManager(conf))
164199

165200
// Verify driverArgs.master is a valid url so that we can use it in ClientActor safely
166-
Master.toAkkaUrl(driverArgs.master, AkkaUtils.protocol(actorSystem))
201+
for (m <- driverArgs.masters) {
202+
Master.toAkkaUrl(m, AkkaUtils.protocol(actorSystem))
203+
}
167204
actorSystem.actorOf(Props(classOf[ClientActor], driverArgs, conf))
168205

169206
actorSystem.awaitTermination()

0 commit comments

Comments
 (0)