ulysses-you commented on code in PR #57329:
URL: https://github.com/apache/spark/pull/57329#discussion_r3614437303


##########
core/src/main/scala/org/apache/spark/scheduler/TaskSchedulerImpl.scala:
##########
@@ -426,23 +426,27 @@ private[spark] class TaskSchedulerImpl(
             val prof = 
sc.resourceProfileManager.resourceProfileFromId(taskSetRpID)
             val taskCpus = 
ResourceProfile.getTaskCpusOrDefaultForProfile(prof, conf)
             val (taskDescOption, didReject, index) =
-              taskSet.resourceOffer(execId, host, maxLocality, taskCpus, 
taskResAssignments)
+              taskSet.resourceOffer(execId, host, maxLocality, taskCpus, 
taskResAssignments,
+                availableCpus(i))
             noDelayScheduleRejects &= !didReject
             for (task <- taskDescOption) {
-              val (locality, resources) = if (task != null) {
+              // The CPUs actually used may exceed the ResourceProfile's 
taskCpus when the task is
+              // an OOM retry (see spark.task.oomRetryCpusIncrement); read it 
from the
+              // TaskDescription so the availableCpus bookkeeping matches what 
was launched.
+              val (locality, resources, cpusUsed) = if (task != null) {
                 tasks(i) += task
                 addRunningTask(task.taskId, execId, taskSet)
-                (taskSet.taskInfos(task.taskId).taskLocality, task.resources)
+                (taskSet.taskInfos(task.taskId).taskLocality, task.resources, 
task.cpus)
               } else {
                 assert(taskSet.isBarrier, "TaskDescription can only be null 
for barrier task")
                 val barrierTask = taskSet.barrierPendingLaunchTasks(index)
                 barrierTask.assignedOfferIndex = i
                 barrierTask.assignedCores = taskCpus
-                (barrierTask.taskLocality, barrierTask.assignedResources)
+                (barrierTask.taskLocality, barrierTask.assignedResources, 
taskCpus)
               }
 
               minLaunchedLocality = minTaskLocality(minLaunchedLocality, 
Some(locality))
-              availableCpus(i) -= taskCpus
+              availableCpus(i) -= cpusUsed

Review Comment:
   Fixed in 8f657a78422. `LocalEndpoint` now tracks each launched task's actual 
cpus in a `taskId -> cpus` map and refunds that exact count on completion, 
instead of the fixed `CPUS_PER_TASK`:
   
   ```scala
   private val runningTaskCpus = new mutable.HashMap[Long, Int]()
   ...
   // StatusUpdate, on finished:
   freeCores += 
runningTaskCpus.remove(taskId).getOrElse(scheduler.CPUS_PER_TASK)
   ...
   // reviveOffers, per launched task:
   freeCores -= task.cpus
   runningTaskCpus(task.taskId) = task.cpus
   ```
   
   Charging `task.cpus` at launch keeps the local free-core count consistent 
with what `TaskSchedulerImpl` debited, so a two-cpu OOM retry no longer leaves 
a phantom core that could oversubscribe the local executor. Added an end-to-end 
test in `FailureSuite` that runs a `local[4,2]` job whose first attempts OOM 
and whose retries need more cpus, asserting the job completes with the expected 
task count.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to