You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ch...@apache.org on 2026/10/14 07:55:43 UTC

(spark) branch branch-4.x updated: [SPARK-59813][YARN] Dump thread stacks when SparkContext initialization times out in ApplicationMaster

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

pan3793 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 e336c2e106d8 [SPARK-59813][YARN] Dump thread stacks when SparkContext initialization times out in ApplicationMaster
e336c2e106d8 is described below

commit e336c2e106d8268e94d16fa6e5823bd942eac592
Author: singer-bin <26...@users.noreply.github.com>
AuthorDate: Wed Oct 14 15:55:13 2026 +0800

    [SPARK-59813][YARN] Dump thread stacks when SparkContext initialization times out in ApplicationMaster
    
    ## What changes were proposed in this pull request?
    
    When SparkContext initialization times out in YARN cluster mode (`spark.yarn.am.waitTime`), the ApplicationMaster fails the application with exit code 13 and only advises to "check earlier log output for errors". However, in real production incidents the driver thread is often blocked *silently* (slow/contended node, a blocking call with no logging, etc.) and there is **no** earlier log output at all, leaving operators with no way to tell where the driver thread was stuck.
    
    This PR logs a full thread dump when the timeout fires, with the user application thread (driver) sorted to the front since it is usually the most relevant one. The behavior is gated by a new config `spark.yarn.am.sparkContextInitTimeout.threadDump` (default `true`), following the same pattern as `spark.task.reaper.threadDump` on the executor side. The dump is best-effort: any failure while capturing it is swallowed so it never masks the original timeout error.
    
    ## Why are the changes needed?
    
    Production example: a long-running SQL engine (Kyuubi) failed with `java.util.concurrent.TimeoutException: Futures timed out after [100000 milliseconds]` and exit code 13. The driver stdout showed ~80 seconds of complete silence right after `SparkEnv` initialization finished. Cross-checking the HDFS NameNode and Hive Metastore logs proved the driver issued zero external I/O during the hang, so the usual suspects (slow NN/HMS calls) were all ruled out — but without a thread dump there  [...]
    
    ## Does this PR introduce any user-facing change?
    
    Yes: a new configuration `spark.yarn.am.sparkContextInitTimeout.threadDump` (default `true`). When enabled, an error-level thread dump is logged on the SparkContext-initialization-timeout failure path.
    
    ## How was this patch tested?
    
    - Built the module and its dependency chain: `./build/mvn -Pyarn -pl resource-managers/yarn -am package -DskipTests`
    - `scalastyle:check` on `resource-managers/yarn` and `checkstyle:check` on `common/utils-java` both pass
    - `ApplicationMasterSuite` passes
    
    The `runDriver` timeout path has no existing test harness (it requires a live YARN RM), and this is a logging-only change on a failure path, so no new test is added.
    
    Closes #58991 from singer-bin/dev/am-timeout-threaddump.
    
    Lead-authored-by: singer-bin <26...@users.noreply.github.com>
    Co-authored-by: singerzhang <si...@tencent.com>
    Signed-off-by: Cheng Pan <ch...@apache.org>
    (cherry picked from commit b4316defc89eff702118411119acbe65454d18d9)
    Signed-off-by: Cheng Pan <ch...@apache.org>
---
 .../spark/deploy/yarn/ApplicationMaster.scala      | 25 ++++++++++++++++++++++
 .../apache/spark/deploy/yarn/config/package.scala  |  8 +++++++
 2 files changed, 33 insertions(+)

diff --git a/resource-managers/yarn/src/main/scala/org/apache/spark/deploy/yarn/ApplicationMaster.scala b/resource-managers/yarn/src/main/scala/org/apache/spark/deploy/yarn/ApplicationMaster.scala
index 9c9f6f3bc313..0cfe0a14ed11 100644
--- a/resource-managers/yarn/src/main/scala/org/apache/spark/deploy/yarn/ApplicationMaster.scala
+++ b/resource-managers/yarn/src/main/scala/org/apache/spark/deploy/yarn/ApplicationMaster.scala
@@ -528,6 +528,9 @@ private[spark] class ApplicationMaster(
         logError(log"SparkContext did not initialize after waiting for " +
             log"${MDC(LogKeys.TIMEOUT, totalWaitTime)} ms. " +
             log"Please check earlier log output for errors. Failing the application.")
+        if (sparkConf.get(AM_SC_INIT_TIMEOUT_THREAD_DUMP)) {
+          dumpThreadStacksOnScInitTimeout()
+        }
         finish(FinalApplicationStatus.FAILED,
           ApplicationMaster.EXIT_SC_NOT_INITED,
           "Timed out waiting for SparkContext.")
@@ -536,6 +539,28 @@ private[spark] class ApplicationMaster(
     }
   }
 
+  /**
+   * Log the stack traces of all threads to help diagnose what the user application thread
+   * (driver) was blocked on when SparkContext initialization timed out. The user thread is
+   * sorted to the front since it is usually the most relevant one. Failures of the dump
+   * itself are swallowed so that they never mask the original timeout error.
+   */
+  private def dumpThreadStacksOnScInitTimeout(): Unit = {
+    try {
+      val dumps = Utils.getThreadDump()
+      val sorted = Option(userClassThread) match {
+        case Some(t) => dumps.sortBy(d => if (d.threadId == t.getId) 0 else 1)
+        case None => dumps
+      }
+      logError(log"Thread dump on SparkContext initialization timeout:\n" +
+        log"${MDC(LogKeys.THREAD, sorted.map(_.toString).mkString("\n"))}")
+    } catch {
+      case NonFatal(dumpError) =>
+        logWarning("Failed to capture thread dump on SparkContext initialization timeout.",
+          dumpError)
+    }
+  }
+
   private def runExecutorLauncher(): Unit = {
     val hostname = Utils.localHostNameForURI()
     val amCores = sparkConf.get(AM_CORES)
diff --git a/resource-managers/yarn/src/main/scala/org/apache/spark/deploy/yarn/config/package.scala b/resource-managers/yarn/src/main/scala/org/apache/spark/deploy/yarn/config/package.scala
index 7147cbfcf276..c6a0a3f18665 100644
--- a/resource-managers/yarn/src/main/scala/org/apache/spark/deploy/yarn/config/package.scala
+++ b/resource-managers/yarn/src/main/scala/org/apache/spark/deploy/yarn/config/package.scala
@@ -251,6 +251,14 @@ package object config extends Logging {
     .timeConf(TimeUnit.MILLISECONDS)
     .createWithDefaultString("100s")
 
+  private[spark] val AM_SC_INIT_TIMEOUT_THREAD_DUMP =
+    ConfigBuilder("spark.yarn.am.sparkContextInitTimeout.threadDump")
+      .doc(s"Whether to print all thread stacks when SparkContext initialization times out " +
+        s"(see ${AM_MAX_WAIT_TIME.key}).")
+      .version("4.4.0")
+      .booleanConf
+      .createWithDefault(true)
+
   private[spark] val YARN_METRICS_NAMESPACE = ConfigBuilder("spark.yarn.metrics.namespace")
     .doc("The root namespace for AM metrics reporting.")
     .version("2.4.0")


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