You are viewing a plain text version of this content. The canonical link for it is here.
Posted to reviews@spark.apache.org by "jshmchenxi (via GitHub)" <gi...@apache.org> on 2024/09/02 13:25:35 UTC

[PR] [SPARK-49479][CORE] Use daemon ScheduledThreadPoolExecutor for BarrierCoordinator [spark]

jshmchenxi opened a new pull request, #47956:
URL: https://github.com/apache/spark/pull/47956

   It is observed that when using Spark Torch Distributor, Spark driver pod could hang around after calling spark.stop(). Although the Spark Context was shutdown, the JVM was still running.
   
   The reason was that there is a non-daemon timer thread named BarrierCoordinator barrier epoch increment timer, which prevented the driver JVM from stopping. In SPARK-46895 we replaced the timer with non-daemon single thread scheduled executor, but the issue still exists.
   
   We should use daemon single thread scheduled executor instead.
   
   <!--
   Thanks for sending a pull request!  Here are some tips for you:
     1. If this is your first time, please read our contributor guidelines: https://spark.apache.org/contributing.html
     2. Ensure you have added or run the appropriate tests for your PR: https://spark.apache.org/developer-tools.html
     3. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP][SPARK-XXXX] Your PR title ...'.
     4. Be sure to keep the PR description updated to reflect all changes.
     5. Please write your PR title to summarize what this PR proposes.
     6. If possible, provide a concise example to reproduce the issue for a faster review.
     7. If you want to add a new configuration, please read the guideline first for naming configurations in
        'core/src/main/scala/org/apache/spark/internal/config/ConfigEntry.scala'.
     8. If you want to add or modify an error type or message, please read the guideline first in
        'common/utils/src/main/resources/error/README.md'.
   -->
   
   ### What changes were proposed in this pull request?
   <!--
   Please clarify what changes you are proposing. The purpose of this section is to outline the changes and how this PR fixes the issue. 
   If possible, please consider writing useful notes for better and faster reviews in your PR. See the examples below.
     1. If you refactor some codes with changing classes, showing the class hierarchy will help reviewers.
     2. If you fix some SQL features, you can provide some references of other DBMSes.
     3. If there is design documentation, please add the link.
     4. If there is a discussion in the mailing list, please add the link.
   -->
   Use daemon `ScheduledThreadPoolExecutor` instead of non-daemon one as timer for `BarrierCoordinator`.
   
   ### Why are the changes needed?
   <!--
   Please clarify why the changes are needed. For instance,
     1. If you propose a new API, clarify the use case for a new API.
     2. If you fix a bug, you can clarify why it is a bug.
   -->
   In Barrier Execution Mode, Spark driver JVM could hang around after calling `spark.stop()`. Although the Spark Context was shutdown, the JVM was still running. The reason was that there is a non-daemon timer thread named `BarrierCoordinator barrier epoch increment timer`, which prevented the driver JVM from stopping.
   
   ### Does this PR introduce _any_ user-facing change?
   <!--
   Note that it means *any* user-facing change including all aspects such as the documentation fix.
   If yes, please clarify the previous behavior and the change this PR proposes - provide the console output, description and/or an example to show the behavior difference if possible.
   If possible, please also clarify if this is a user-facing change compared to the released Spark versions or within the unreleased branches such as master.
   If no, write 'No'.
   -->
   No
   
   ### How was this patch tested?
   <!--
   If tests were added, say they were added here. Please make sure to add some test cases that check the changes thoroughly including negative and positive cases if possible.
   If it was tested in a way different from regular unit tests, please clarify how you tested step by step, ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future.
   If tests were not added, please describe why they were not added and/or why it was difficult to add.
   If benchmark tests were added, please run the benchmarks in GitHub Actions for the consistent environment, and the instructions could accord to: https://spark.apache.org/developer-tools.html#github-workflow-benchmarks.
   -->
   Manual test.
   
   Run [barrier_example.py](https://gist.github.com/jshmchenxi/5d7e7c61e1eedd03cd6d676699059e9b#file-barrier_example-py) script locally using `./bin/spark-submit barrier_example.py`. Without this change, the JVM would hang there and not exit. With this change it would exit successuflly.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   <!--
   If generative AI tooling has been used in the process of authoring this patch, please include the
   phrase: 'Generated-by: ' followed by the name of the tool and its version.
   If no, write 'No'.
   Please refer to the [ASF Generative Tooling Guidance](https://www.apache.org/legal/generative-tooling.html) for details.
   -->
   No


-- 
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: reviews-unsubscribe@spark.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


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


Re: [PR] [SPARK-49479][CORE] Use daemon ScheduledThreadPoolExecutor for BarrierCoordinator [spark]

Posted by "mridulm (via GitHub)" <gi...@apache.org>.
mridulm commented on PR #47956:
URL: https://github.com/apache/spark/pull/47956#issuecomment-2327893152

   `BarrierCoordinator.onStop` should have terminated it - do we have additional details on why this did not happen ?


-- 
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: reviews-unsubscribe@spark.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


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


Re: [PR] [SPARK-49479][CORE] Use daemon ScheduledThreadPoolExecutor for BarrierCoordinator [spark]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] closed pull request #47956: [SPARK-49479][CORE] Use daemon ScheduledThreadPoolExecutor for BarrierCoordinator
URL: https://github.com/apache/spark/pull/47956


-- 
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: reviews-unsubscribe@spark.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


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


Re: [PR] [SPARK-49479][CORE] Use daemon ScheduledThreadPoolExecutor for BarrierCoordinator [spark]

Posted by "mridulm (via GitHub)" <gi...@apache.org>.
mridulm commented on PR #47956:
URL: https://github.com/apache/spark/pull/47956#issuecomment-2328035086

   So the issue here is the mixing of two different api's - `TimerTask` and `ScheduledExecutorService`.
   The original code, which @jiangxb1987 added, used `Timer` and `TimerTask` consistently.
   
   Hence, when `timerTask.cancel` was invoked, the corresponding timer was cancelled - and things worked as expected.
   
   In SPARK-46895, `Timer` was replaced with `ScheduledExecutorService` in various cases - and that is where the bug unfortunately came in.
   
   `TimerTask` is also a `Runnable`, and so can be passed to `schedule*` methods in `ScheduledExecutorService`.
   But unfortunately `TimerTask.cancel` is effectively a noop when it is scheduled using `ScheduledExecutorService`.
   
   That is what causes the issue here.
   
   
   To test, the following diff ensures that the attached test that @jshmchenxi provided works fine (it is just a POC, not a complete fix).
   ```
   diff --git a/core/src/main/scala/org/apache/spark/BarrierCoordinator.scala b/core/src/main/scala/org/apache/spark/BarrierCoordinator.scala
   index adce6c3f5ffd..be8a4bd831cc 100644
   --- a/core/src/main/scala/org/apache/spark/BarrierCoordinator.scala
   +++ b/core/src/main/scala/org/apache/spark/BarrierCoordinator.scala
   @@ -17,8 +17,7 @@
    
    package org.apache.spark
    
   -import java.util.TimerTask
   -import java.util.concurrent.{ConcurrentHashMap, TimeUnit}
   +import java.util.concurrent.{ConcurrentHashMap, ScheduledFuture, TimeUnit}
    import java.util.function.Consumer
    
    import scala.collection.mutable.{ArrayBuffer, HashSet}
   @@ -77,12 +76,23 @@ private[spark] class BarrierCoordinator(
    
      override def onStop(): Unit = {
        try {
   +      logInfo("onStop called ...")
          states.forEachValue(1, clearStateConsumer)
   +      logInfo("states.forEachValue done ...")
          states.clear()
   +      logInfo("states.clear done ...")
          listenerBus.removeListener(listener)
   +      logInfo("listenerBus.removeListener done ...")
          ThreadUtils.shutdown(timer)
   +      logInfo("ThreadUtils.shutdown done ...")
   +    } catch {
   +      case th: Throwable =>
   +        logInfo("Unexpected ...", th)
   +        throw th
        } finally {
   +      logInfo("super.onStop called ...")
          super.onStop()
   +      logInfo("super.onStop called ... DONE")
        }
      }
    
   @@ -117,11 +127,12 @@ private[spark] class BarrierCoordinator(
        private val requestMethods = new HashSet[RequestMethod.Value]
    
        // A timer task that ensures we may timeout for a barrier() call.
   -    private var timerTask: TimerTask = null
   +    private var timerTask: Runnable = null
   +    private var timerTaskFuture: ScheduledFuture[_] = null
    
        // Init a TimerTask for a barrier() call.
        private def initTimerTask(state: ContextBarrierState): Unit = {
   -      timerTask = new TimerTask {
   +      timerTask = new Runnable {
            override def run(): Unit = state.synchronized {
              // Timeout current barrier() call, fail all the sync requests.
              requesters.foreach(_.sendFailure(new SparkException("The coordinator didn't get all " +
   @@ -134,10 +145,10 @@ private[spark] class BarrierCoordinator(
    
        // Cancel the current active TimerTask and release resources.
        private def cancelTimerTask(): Unit = {
   -      if (timerTask != null) {
   -        timerTask.cancel()
   +      if (timerTaskFuture != null) {
   +        timerTaskFuture.cancel(true)
            timer.purge()
   -        timerTask = null
   +        timerTaskFuture = null
          }
        }
    
   @@ -173,7 +184,7 @@ private[spark] class BarrierCoordinator(
            // we may timeout for the sync.
            if (requesters.isEmpty) {
              initTimerTask(this)
   -          timer.schedule(timerTask, timeoutInSecs, TimeUnit.SECONDS)
   +          timerTaskFuture = timer.schedule(timerTask, timeoutInSecs, TimeUnit.SECONDS)
            }
            // Add the requester to array of RPCCallContexts pending for reply.
            requesters += requester
   diff --git a/core/src/main/scala/org/apache/spark/BarrierTaskContext.scala b/core/src/main/scala/org/apache/spark/BarrierTaskContext.scala
   index c8d6000cd628..e794ae030c38 100644
   --- a/core/src/main/scala/org/apache/spark/BarrierTaskContext.scala
   +++ b/core/src/main/scala/org/apache/spark/BarrierTaskContext.scala
   @@ -17,7 +17,7 @@
    
    package org.apache.spark
    
   -import java.util.{Properties, TimerTask}
   +import java.util.Properties
    import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit}
    
    import scala.concurrent.duration._
   @@ -72,7 +72,7 @@ class BarrierTaskContext private[spark] (
        logTrace("Current callSite: " + Utils.getCallSite())
    
        val startTime = System.currentTimeMillis()
   -    val timerTask = new TimerTask {
   +    val timerTask = new Runnable {
          override def run(): Unit = {
            logProgressInfo(
              log"waiting under the global sync since ${MDC(TIME, startTime)}",
   @@ -81,7 +81,7 @@ class BarrierTaskContext private[spark] (
          }
        }
        // Log the update of global sync every 1 minute.
   -    timer.scheduleAtFixedRate(timerTask, 1, 1, TimeUnit.MINUTES)
   +    val future = timer.scheduleAtFixedRate(timerTask, 1, 1, TimeUnit.MINUTES)
    
        try {
          val abortableRpcFuture = barrierCoordinator.askAbortable[Array[String]](
   @@ -120,7 +120,7 @@ class BarrierTaskContext private[spark] (
            logProgressInfo(log"failed to perform global sync", Some(startTime))
            throw e
        } finally {
   -      timerTask.cancel()
   +      future.cancel(true)
          timer.purge()
        }
      }
   ```
   
   Note that there are other cases where this happens ... and simply changing to `newDaemonSingleThreadScheduledExecutor` only masks the problem - does not fix it (it results in timer tasks continuing to run in the background, and keep growing as more tasks get scheduled).
   
   +CC @jiangxb1987 who wrote the initial impl, and @LuciferYang who merged SPARK-46895 for thoughts


-- 
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: reviews-unsubscribe@spark.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


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


Re: [PR] [SPARK-49479][CORE] Use daemon ScheduledThreadPoolExecutor for BarrierCoordinator [spark]

Posted by "jshmchenxi (via GitHub)" <gi...@apache.org>.
jshmchenxi commented on PR #47956:
URL: https://github.com/apache/spark/pull/47956#issuecomment-2324817682

   This commit depends on #47957 and #47945 for fixing SPARK-49479 in branch 3.5.


-- 
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: reviews-unsubscribe@spark.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


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


Re: [PR] [SPARK-49479][CORE] Use daemon ScheduledThreadPoolExecutor for BarrierCoordinator [spark]

Posted by "jshmchenxi (via GitHub)" <gi...@apache.org>.
jshmchenxi commented on PR #47956:
URL: https://github.com/apache/spark/pull/47956#issuecomment-2324830349

   @LuciferYang @yaooqinn Please take a look, thanks!


-- 
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: reviews-unsubscribe@spark.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


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


Re: [PR] [SPARK-49479][CORE] Use daemon ScheduledThreadPoolExecutor for BarrierCoordinator [spark]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #47956:
URL: https://github.com/apache/spark/pull/47956#issuecomment-2557910736

   We're closing this PR because it hasn't been updated in a while. This isn't a judgement on the merit of the PR in any way. It's just a way of keeping the PR queue manageable.
   If you'd like to revive this PR, please reopen it and ask a committer to remove the Stale tag!


-- 
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: reviews-unsubscribe@spark.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


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