-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-15714][CORE] Fix flaky o.a.s.scheduler.BlacklistIntegrationSuite #13454
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 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
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
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 |
|---|---|---|
|
|
@@ -133,6 +133,11 @@ abstract class SchedulerIntegrationSuite[T <: MockBackend: ClassTag] extends Spa | |
| // when the job succeeds | ||
| assert(taskScheduler.runningTaskSets.isEmpty) | ||
| assert(!backend.hasTasks) | ||
| } else { | ||
| // Note that we CANNOT check for empty results on a failure -- the resultHandler will | ||
| // record results from successful tasks, even if the job fails overall. We just check | ||
| // that we got a failure. | ||
| assert(failure != null) | ||
| } | ||
| assert(scheduler.activeJobs.isEmpty) | ||
| } | ||
|
|
@@ -217,10 +222,10 @@ private[spark] abstract class MockBackend( | |
| * Test backends should call this to get a task that has been assigned to them by the scheduler. | ||
| * Each task should be responded to with either [[taskSuccess]] or [[taskFailed]]. | ||
| */ | ||
| def beginTask(): TaskDescription = { | ||
| def beginTask(): (TaskDescription, Task[_]) = { | ||
| synchronized { | ||
| val toRun = assignedTasksWaitingToRun.remove(assignedTasksWaitingToRun.size - 1) | ||
| runningTasks += toRun | ||
| runningTasks += toRun._1.taskId | ||
| toRun | ||
| } | ||
| } | ||
|
|
@@ -255,7 +260,7 @@ private[spark] abstract class MockBackend( | |
| taskScheduler.statusUpdate(task.taskId, state, resultBytes) | ||
| if (TaskState.isFinished(state)) { | ||
| synchronized { | ||
| runningTasks -= task | ||
| runningTasks -= task.taskId | ||
| executorIdToExecutor(task.executorId).freeCores += taskScheduler.CPUS_PER_TASK | ||
| freeCores += taskScheduler.CPUS_PER_TASK | ||
| } | ||
|
|
@@ -264,9 +269,9 @@ private[spark] abstract class MockBackend( | |
| } | ||
|
|
||
| // protected by this | ||
| private val assignedTasksWaitingToRun = new ArrayBuffer[TaskDescription](10000) | ||
| private val assignedTasksWaitingToRun = new ArrayBuffer[(TaskDescription, Task[_])](10000) | ||
| // protected by this | ||
| private val runningTasks = ArrayBuffer[TaskDescription]() | ||
| private val runningTasks = HashSet[Long]() | ||
|
|
||
| def hasTasks: Boolean = synchronized { | ||
| assignedTasksWaitingToRun.nonEmpty || runningTasks.nonEmpty | ||
|
|
@@ -307,10 +312,19 @@ private[spark] abstract class MockBackend( | |
| */ | ||
| override def reviveOffers(): Unit = { | ||
| val offers: Seq[WorkerOffer] = generateOffers() | ||
| val newTasks = taskScheduler.resourceOffers(offers).flatten | ||
| val newTaskDescriptions = taskScheduler.resourceOffers(offers).flatten | ||
| // get the task now, since that requires a lock on TaskSchedulerImpl, to prevent individual | ||
| // tests for introducing a race if they need it | ||
|
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. s/for/from |
||
| val newTasks = taskScheduler.synchronized { | ||
| newTaskDescriptions.map { taskDescription => | ||
| val taskSet = taskScheduler.taskIdToTaskSetManager(taskDescription.taskId).taskSet | ||
| val task = taskSet.tasks(taskDescription.index) | ||
| (taskDescription, task) | ||
| } | ||
| } | ||
| synchronized { | ||
| newTasks.foreach { task => | ||
| executorIdToExecutor(task.executorId).freeCores -= taskScheduler.CPUS_PER_TASK | ||
| newTasks.foreach { case (taskDescription, task) => | ||
| executorIdToExecutor(taskDescription.executorId).freeCores -= taskScheduler.CPUS_PER_TASK | ||
| } | ||
| freeCores -= newTasks.size * taskScheduler.CPUS_PER_TASK | ||
| assignedTasksWaitingToRun ++= newTasks | ||
|
|
@@ -437,8 +451,8 @@ class BasicSchedulerIntegrationSuite extends SchedulerIntegrationSuite[SingleCor | |
| */ | ||
| testScheduler("super simple job") { | ||
| def runBackend(): Unit = { | ||
| val task = backend.beginTask() | ||
| backend.taskSuccess(task, 42) | ||
| val (taskDescripition, task) = backend.beginTask() | ||
|
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.
|
||
| backend.taskSuccess(taskDescripition, 42) | ||
| } | ||
| withBackend(runBackend _) { | ||
| val jobFuture = submit(new MockRDD(sc, 10, Nil), (0 until 10).toArray) | ||
|
|
@@ -473,9 +487,7 @@ class BasicSchedulerIntegrationSuite extends SchedulerIntegrationSuite[SingleCor | |
| val d = join(30, b, c) | ||
|
|
||
| def runBackend(): Unit = { | ||
| val taskDescription = backend.beginTask() | ||
| val taskSet = taskScheduler.taskIdToTaskSetManager(taskDescription.taskId).taskSet | ||
| val task = taskSet.tasks(taskDescription.index) | ||
| val (taskDescription, task) = backend.beginTask() | ||
|
|
||
| // make sure the required map output is available | ||
| task.stageId match { | ||
|
|
@@ -515,9 +527,7 @@ class BasicSchedulerIntegrationSuite extends SchedulerIntegrationSuite[SingleCor | |
| val stageToAttempts = new HashMap[Int, HashSet[Int]]() | ||
|
|
||
| def runBackend(): Unit = { | ||
| val taskDescription = backend.beginTask() | ||
| val taskSet = taskScheduler.taskIdToTaskSetManager(taskDescription.taskId).taskSet | ||
| val task = taskSet.tasks(taskDescription.index) | ||
| val (taskDescription, task) = backend.beginTask() | ||
| stageToAttempts.getOrElseUpdate(task.stageId, new HashSet()) += task.stageAttemptId | ||
|
|
||
| // make sure the required map output is available | ||
|
|
@@ -549,16 +559,15 @@ class BasicSchedulerIntegrationSuite extends SchedulerIntegrationSuite[SingleCor | |
|
|
||
| testScheduler("job failure after 4 attempts") { | ||
| def runBackend(): Unit = { | ||
| val task = backend.beginTask() | ||
| backend.taskFailed(task, new RuntimeException("test task failure")) | ||
| val (taskDescription, task) = backend.beginTask() | ||
| backend.taskFailed(taskDescription, new RuntimeException("test task failure")) | ||
| } | ||
| withBackend(runBackend _) { | ||
| val jobFuture = submit(new MockRDD(sc, 10, Nil), (0 until 10).toArray) | ||
| val duration = Duration(1, SECONDS) | ||
| Await.ready(jobFuture, duration) | ||
| failure.getMessage.contains("test task failure") | ||
| } | ||
| assert(results.isEmpty) | ||
| assertDataStructuresEmpty(noFailure = false) | ||
| } | ||
| } | ||
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.
It's hard to correlate this comment with the code around it. It seems like it belongs in the call site, where the
resultslist was actually being checked (and you removed the assert). But without the assert, the comment would be out of place there too.I just find it very improbable that someone will read this comment when writing a test. It would help if, instead, the expected results were passed to this method, so that the asserts happen here instead of in the body of every test. Then the comment is fine where it is, because this method would handle the asserts (or lack thereof).
Also, the same assert exists in the "job failure after 4 attempts" test in
SchedulerIntegrationSuite. Doesn't that need to change too?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.
Yeah I agree this is a little weird, its not really going to help somebody writing a test, only if somebody looks at the entire framework here a little more closely. I thought of passing in expected results or expected failure, but maybe in some test you don't want to check the expectation exactly. I guess somebody writing a test at least needs to understand how to use
assertDataStructuresEmpty,results, andfailure. I'll put comments there instead, someone writing a test is at least more likely to read those comments.I'm not totally sure I know what you mean about "job failure after 4 attempts" -- I did remove the
assert(results.isEmpty)there. Actually, that assert is fine (all tasks fail, so there are never any results), but I thought it would be better to remove it in case someone copies that code into another test without thinking about it.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.
Ah, my bad, I swear I searched for it in the diff and the browser didn't find it.