You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by do...@apache.org on 2026/09/22 19:16:37 UTC

(spark) branch branch-4.x updated: [SPARK-59714][CORE] Align `TaskSchedulerImpl.executorDecommission` locking with `executorLost`

This is an automated email from the ASF dual-hosted git repository.

dongjoon-hyun pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new 5b218b5ec2dd [SPARK-59714][CORE] Align `TaskSchedulerImpl.executorDecommission` locking with `executorLost`
5b218b5ec2dd is described below

commit 5b218b5ec2dd0e98fe689a5738a8c2e3b96fdf34
Author: Dongjoon Hyun <do...@apache.org>
AuthorDate: Tue Sep 22 12:16:18 2026 -0700

    [SPARK-59714][CORE] Align `TaskSchedulerImpl.executorDecommission` locking with `executorLost`
    
    ### What changes were proposed in this pull request?
    
    This PR moves `rootPool.executorDecommission(executorId)` inside the `synchronized` block that
    `TaskSchedulerImpl.executorDecommission()` already has, keeping `backend.reviveOffers()` outside it.
    
    ### Why are the changes needed?
    
    `rootPool.executorDecommission()` reaches `TaskSetManager.recomputeLocality()`, and
    `TaskSchedulerImpl` documents that "TaskSetManagers are not thread safe, so any access to one should
    be synchronized on this class". The sibling `executorLost()` path already follows this: it calls
    `rootPool.executorLost()` inside the lock and keeps only the `dagScheduler` and `backend` calls
    outside. This makes the two paths consistent.
    
    There is no behavior change. The only production caller,
    `CoarseGrainedSchedulerBackend.decommissionExecutors()`, is `withLock`, which already holds this
    same monitor, so the call was effectively synchronized in practice. This PR makes the method
    self-contained instead of relying on its caller.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    Pass the CIs.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 5
    
    Closes #58970 from dongjoon-hyun/SPARK-59714.
    
    Authored-by: Dongjoon Hyun <do...@apache.org>
    Signed-off-by: Dongjoon Hyun <do...@apache.org>
    (cherry picked from commit 582e28bf7074598ac85a0b7de9c5916c44cc5661)
    Signed-off-by: Dongjoon Hyun <do...@apache.org>
---
 .../main/scala/org/apache/spark/scheduler/TaskSchedulerImpl.scala    | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/core/src/main/scala/org/apache/spark/scheduler/TaskSchedulerImpl.scala b/core/src/main/scala/org/apache/spark/scheduler/TaskSchedulerImpl.scala
index 4600993d2664..d1eb646fc316 100644
--- a/core/src/main/scala/org/apache/spark/scheduler/TaskSchedulerImpl.scala
+++ b/core/src/main/scala/org/apache/spark/scheduler/TaskSchedulerImpl.scala
@@ -1059,8 +1059,11 @@ private[spark] class TaskSchedulerImpl(
         executorsPendingDecommission(executorId) =
           ExecutorDecommissionState(clock.getTimeMillis(), decommissionInfo.workerHost)
       }
+      // This reaches TaskSetManager.recomputeLocality(), and TaskSetManagers are not thread
+      // safe, so it must be called while holding the lock, like rootPool.executorLost() is.
+      rootPool.executorDecommission(executorId)
     }
-    rootPool.executorDecommission(executorId)
+    // Call backend.reviveOffers() without holding the lock on this to prevent deadlock
     backend.reviveOffers()
   }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org