Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -1107,10 +1107,16 @@ private[spark] class TaskSetManager(
def recomputeLocality(): Unit = {
// A zombie TaskSetManager may reach here while executorLost happens
if (isZombie) return
val previousLocalityIndex = currentLocalityIndex
val previousLocalityLevel = myLocalityLevels(currentLocalityIndex)
myLocalityLevels = computeValidLocalityLevels()
localityWaits = myLocalityLevels.map(getLocalityWait)
currentLocalityIndex = getLocalityIndex(previousLocalityLevel)
if (currentLocalityIndex > previousLocalityIndex) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should change this to only happen when executorAdded. this is also called on lost and decommission and it doesn't make sense to go to "lower" level. Note we may want to stay away from saying higher in the comment below. The code values, lower is actually more strict - meaning process is lowest value. so perhaps don't say higher or lower but say more local or less local. Perhaps pass in parameter from executorAdded.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should change this to only happen when executorAdded. this is also called on lost and decommission and it doesn't make sense to go to "lower" level.

I think it's impossible to go to "lower" or more local level in case of lost and decommission. Lost and decommission would remove executors, so the locality levels can only be less compared to the previous locality levels. It also means, lost and decommission will not add new more local levels.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so perhaps don't say higher or lower but say more local or less local.

Yea, good point!

// SPARK-31837: there's new higher locality level, so shift to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about If the new level is more local, shift to the most local level?

// the highest locality level in terms of better data locality
currentLocalityIndex = 0

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only concern of this change is, for general cases, whether the task set would take more time on delay scheduling and thus reduce the throughput of the cluster. But considering the improvement that we gain(to increase the throughput of the cluster) from the new version of the delay scheduling, I think it should not be a big problem.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have concerns with this. This is going to reset it back to highest level all the time and then you will have to work your way back through the levels, waiting at each level. This could really delay things a lot if you are down to the ANY level and you have to wait 3 seconds between each one. The problem you are describing is only on startup when you have no executors, correct? Perhaps we can look at something more specific for that.

}
}

def executorAdded(): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,6 @@ class BarrierTaskContextSuite extends SparkFunSuite with LocalSparkContext with

test("SPARK-31485: barrier stage should fail if only partial tasks are launched") {
initLocalClusterSparkContext(2)
// It's required to reset the delay timer when a task is scheduled, otherwise all the tasks
// could get scheduled at ANY level.
sc.conf.set(config.LEGACY_LOCALITY_WAIT_RESET, true)
val rdd0 = sc.parallelize(Seq(0, 1, 2, 3), 2)
val dep = new OneToOneDependency[Int](rdd0)
// set up a barrier stage with 2 tasks and both tasks prefer executor 0 (only 1 core) for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ class TaskSetManagerSuite
manager.executorAdded()
sched.addExecutor("execC", "host2")
manager.executorAdded()
assert(manager.resourceOffer("exec1", "host1", ANY)._1.isDefined)
assert(manager.resourceOffer("execB", "host1", ANY)._1.isDefined)
sched.removeExecutor("execA")
manager.executorLost(
"execA",
Expand All @@ -634,6 +634,25 @@ class TaskSetManagerSuite
assert(sched.taskSetsFailed.contains(taskSet.id))
}

test("SPARK-31837: Shift to the new highest locality level if there is when recomputeLocality") {
sc = new SparkContext("local", "test")
sched = new FakeTaskScheduler(sc)
val taskSet = FakeTask.createTaskSet(2,
Seq(TaskLocation("host1", "execA")),
Seq(TaskLocation("host1", "execA")))
val clock = new ManualClock()
val manager = new TaskSetManager(sched, taskSet, 1, clock = clock)
// before any executors are added to TaskScheduler, the manager's
// locality level only has ANY, so tasks can be scheduled anyway.
assert(manager.resourceOffer("execB", "host2", ANY)._1.isDefined)
sched.addExecutor("execA", "host1")
manager.executorAdded()
// after adding a new executor, the manager locality has PROCESS_LOCAL, NODE_LOCAL, ANY.
// And we'll shift to the new highest locality level, which is PROCESS_LOCAL in this case.
assert(manager.resourceOffer("execC", "host3", ANY)._1.isEmpty)
assert(manager.resourceOffer("execA", "host1", ANY)._1.isDefined)
}

test("test RACK_LOCAL tasks") {
// Assign host1 to rack1
FakeRackUtil.assignHostToRack("host1", "rack1")
Expand Down