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

chengpan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new 15645d8d20c7 [SPARK-55831][YARN] Support 
`spark.yarn.am.defaultJavaOptions`
15645d8d20c7 is described below

commit 15645d8d20c7ecf9147d64818d7d9363182aa3bd
Author: Cheng Pan <[email protected]>
AuthorDate: Thu Mar 5 10:35:15 2026 +0800

    [SPARK-55831][YARN] Support `spark.yarn.am.defaultJavaOptions`
    
    ### What changes were proposed in this pull request?
    
    Introduce a new config `spark.yarn.am.defaultJavaOptions`, if set, it will 
be prepended to the `spark.yarn.am.extraJavaOptions`. Obviously, this only 
takes effect on YARN client mode.
    
    ### Why are the changes needed?
    
    Currently, we have both `defaultJavaOptions` and `extraJavaOptions` for 
driver and executor, but only have `extraJavaOptions` for YARN AM, this is not 
intuitive. I also found AI sometimes suggests such a non-existent config.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    New UT is added.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    No.
    
    Closes #54618 from pan3793/SPARK-55831.
    
    Authored-by: Cheng Pan <[email protected]>
    Signed-off-by: Cheng Pan <[email protected]>
---
 docs/running-on-yarn.md                            | 17 +++++-
 .../apache/spark/deploy/yarn/config/package.scala  | 11 +++-
 .../org/apache/spark/deploy/yarn/ClientSuite.scala | 65 +++++++++++++++++++++-
 3 files changed, 90 insertions(+), 3 deletions(-)

diff --git a/docs/running-on-yarn.md b/docs/running-on-yarn.md
index 465f3a9d075a..d42e5eb4257e 100644
--- a/docs/running-on-yarn.md
+++ b/docs/running-on-yarn.md
@@ -406,6 +406,19 @@ To use a custom metrics.properties for the application 
master and executors, upd
   </td>
   <td>1.2.0</td>
 </tr>
+<tr>
+  <td><code>spark.yarn.am.defaultJavaOptions</code></td>
+  <td>(none)</td>
+  <td>
+  A string of default JVM options to prepend to 
<code>spark.yarn.am.extraJavaOptions</code>
+  for the YARN Application Master in client mode. Note that it is illegal to 
set maximum
+  heap size (-Xmx) settings with this option. Maximum heap size settings can 
be set with
+  <code>spark.yarn.am.memory</code>.
+
+  This is intended to be set by administrators.
+  </td>
+  <td>4.2.0</td>
+</tr>
 <tr>
   <td><code>spark.yarn.am.extraJavaOptions</code></td>
   <td>(none)</td>
@@ -413,7 +426,9 @@ To use a custom metrics.properties for the application 
master and executors, upd
   A string of extra JVM options to pass to the YARN Application Master in 
client mode.
   In cluster mode, use <code>spark.driver.extraJavaOptions</code> instead. 
Note that it is illegal
   to set maximum heap size (-Xmx) settings with this option. Maximum heap size 
settings can be set
-  with <code>spark.yarn.am.memory</code>
+  with <code>spark.yarn.am.memory</code>.
+
+  <code>spark.yarn.am.defaultJavaOptions</code> will be prepended to this 
configuration.
   </td>
   <td>1.3.0</td>
 </tr>
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 f719ca4677af..8b63d117e7fa 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
@@ -301,8 +301,17 @@ package object config extends Logging {
     .intConf
     .createWithDefault(1)
 
+  private[spark] val AM_DEFAULT_JAVA_OPTIONS = 
ConfigBuilder("spark.yarn.am.defaultJavaOptions")
+    .doc("Default Java options for the client-mode AM to prepend to " +
+      "`spark.yarn.am.extraJavaOptions`. This is intended to be set by 
administrators.")
+    .version("4.2.0")
+    .stringConf
+    .createOptional
+
   private[spark] val AM_JAVA_OPTIONS = 
ConfigBuilder("spark.yarn.am.extraJavaOptions")
-    .doc("Extra Java options for the client-mode AM.")
+    .withPrepended(AM_DEFAULT_JAVA_OPTIONS.key)
+    .doc("Extra Java options for the client-mode AM. " +
+      s"`${AM_DEFAULT_JAVA_OPTIONS.key}` will be prepended to this 
configuration.")
     .version("1.3.0")
     .stringConf
     .createOptional
diff --git 
a/resource-managers/yarn/src/test/scala/org/apache/spark/deploy/yarn/ClientSuite.scala
 
b/resource-managers/yarn/src/test/scala/org/apache/spark/deploy/yarn/ClientSuite.scala
index 93d6cc474d20..918cb790bdc9 100644
--- 
a/resource-managers/yarn/src/test/scala/org/apache/spark/deploy/yarn/ClientSuite.scala
+++ 
b/resource-managers/yarn/src/test/scala/org/apache/spark/deploy/yarn/ClientSuite.scala
@@ -46,19 +46,22 @@ import org.apache.hadoop.yarn.util.Records
 import org.mockito.ArgumentMatchers.{any, anyBoolean, eq => meq}
 import org.mockito.Mockito._
 import org.mockito.invocation.InvocationOnMock
+import org.scalatest.PrivateMethodTester
 import org.scalatest.matchers.must.Matchers
 import org.scalatest.matchers.should.Matchers._
 
 import org.apache.spark.{SparkConf, SparkException, SparkFunSuite, TestUtils}
 import org.apache.spark.deploy.yarn.config._
 import org.apache.spark.internal.config._
+import org.apache.spark.launcher.SparkLauncher
 import org.apache.spark.resource.ResourceID
 import org.apache.spark.resource.ResourceUtils.AMOUNT
 import org.apache.spark.util.{SparkConfWithEnv, Utils}
 
 class ClientSuite extends SparkFunSuite
     with Matchers
-    with ResourceRequestTestHelper {
+    with ResourceRequestTestHelper
+    with PrivateMethodTester {
   private def doReturn(value: Any) = org.mockito.Mockito.doReturn(value, 
Seq.empty: _*)
 
   import Client._
@@ -755,6 +758,66 @@ class ClientSuite extends SparkFunSuite
     env("SPARK_USER") should be ("overrideuser")
   }
 
+  test("YARN AM JavaOptions") {
+    Seq("client", "cluster").foreach { deployMode =>
+      withTempDir { stagingDir =>
+        val sparkConf = new SparkConfWithEnv(
+          Map("SPARK_HOME" -> System.getProperty("spark.test.home")))
+          .set(SUBMIT_DEPLOY_MODE, deployMode)
+          .set(SparkLauncher.DRIVER_DEFAULT_JAVA_OPTIONS, "-Dx=1 -Dy=2")
+          .set(SparkLauncher.DRIVER_EXTRA_JAVA_OPTIONS, "-Dz=3")
+          .set(AM_DEFAULT_JAVA_OPTIONS, "-Da=1 -Db=2")
+          .set(AM_JAVA_OPTIONS, "-Dc=3")
+
+        val client = createClient(sparkConf)
+        val appIdField = classOf[Client]
+          .getDeclaredField("org$apache$spark$deploy$yarn$Client$$appId")
+        appIdField.setAccessible(true)
+        // A dummy ApplicationId impl, only `toString` method will be called
+        // in Client.createContainerLaunchContext
+        appIdField.set(client, new ApplicationId {
+          override def getId: Int = 1
+          override def setId(i: Int): Unit = {}
+          override def getClusterTimestamp: Long = 1770077136288L
+          override def setClusterTimestamp(l: Long): Unit = {}
+          override def build(): Unit = {}
+          override def toString: String = "application_1770077136288_0001"
+        })
+        val stagingDirPathField = classOf[Client]
+          
.getDeclaredField("org$apache$spark$deploy$yarn$Client$$stagingDirPath")
+        stagingDirPathField.setAccessible(true)
+        stagingDirPathField.set(client, new Path(stagingDir.getAbsolutePath))
+        val _createContainerLaunchContext =
+          
PrivateMethod[ContainerLaunchContext](Symbol("createContainerLaunchContext"))
+        val containerLaunchContext = client invokePrivate 
_createContainerLaunchContext()
+
+        val commands = containerLaunchContext.getCommands.asScala
+        deployMode match {
+          case "client" =>
+            // In client mode, spark.yarn.am.defaultJavaOptions and 
spark.yarn.am.extraJavaOptions
+            // should be set in AM container command JAVA_OPTIONS
+            commands should contain("'-Da=1'")
+            commands should contain("'-Db=2'")
+            commands should contain("'-Dc=3'")
+            commands should not contain "'-Dx=1'"
+            commands should not contain "'-Dy=2'"
+            commands should not contain "'-Dz=3'"
+          case "cluster" =>
+            // In cluster mode, spark.driver.defaultJavaOptions and 
spark.driver.extraJavaOptions
+            // should be set in AM container command JAVA_OPTIONS
+            commands should not contain "'-Da=1'"
+            commands should not contain "'-Db=2'"
+            commands should not contain "'-Dc=3'"
+            commands should contain ("'-Dx=1'")
+            commands should contain ("'-Dy=2'")
+            commands should contain ("'-Dz=3'")
+          case m =>
+            fail(s"Unexpected deploy mode: $m")
+        }
+      }
+    }
+  }
+
   private val matching = Seq(
     ("files URI match test1", "file:///file1", "file:///file2"),
     ("files URI match test2", "file:///c:file1", "file://c:file2"),


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

Reply via email to