spark-test commented on code in PR #59977:
URL: https://github.com/apache/spark/pull/59977#discussion_r4461899828


##########
sql/core/src/main/scala/org/apache/spark/sql/artifact/ArtifactManager.scala:
##########
@@ -74,20 +74,22 @@ class ArtifactManager(session: SparkSession) extends 
AutoCloseable with Logging
 
   private val ivyCachePath = artifactPath.resolve(".ivy-cache")
 
+  private def ivyConfigReader: ConfigReader =
+    new ConfigReader(new SparkConfigProvider(session.conf.getAllAsJava))
+
   private lazy val runtimeDependencyResolver = {
-    val sparkConf = session.sparkContext.getConf
     val ivyPath = Files.createDirectories(ivyCachePath).toString
     new RuntimeDependencyResolver(
-      ivySettingsPath = sparkConf.get(JAR_IVY_SETTING_PATH),
+      ivySettingsPath = JAR_IVY_SETTING_PATH.readFrom(ivyConfigReader),

Review Comment:
   **Non-blocking (P2):** This resolver, and the two timeout vals below it, now 
read `spark.jars.ivySettings`, `spark.jars.ivy` and the Ivy timeouts (with 
`${...}` expansion) from the session configuration. The same `ArtifactManager` 
also serves Spark Connect's server-side Maven resolution: 
`SparkConnectAddArtifactsHandler` resolves client-sent `MAVEN_DEPENDENCY` 
entries through `holder.artifactManager.resolveArtifacts(uri, 
RejectRequestedRepositories, ...)` and caps these timeouts by the request 
deadline. On a Connect server the session configuration is writable by the 
client. That is why `SparkConnectConfigHandler` reads the redaction pattern 
from the SparkConf instead.
   
   So a client can send `ConfigRequest.Set` (or SQL `SET`) with 
`spark.sql.legacy.setCommandRejectsSparkCoreConfs=false`, which is a runtime 
SQL conf, followed by `spark.jars.ivySettings=''`. If the session has not 
resolved an `ivy://` dependency yet, its next `MAVEN_DEPENDENCY` entry then 
resolves through the built-in resolver chain (local caches, Maven Central, 
spark-packages) instead of the operator's settings file. The same steps let it 
select another Ivy settings file or local repository root on the driver host, 
or raise both timeouts up to `Int.MaxValue` ms when the request has no 
deadline. Without the flag, it can still redirect an operator value that uses 
`${spark.<unregistered key>}` by setting that key. On the base branch all four 
values came from the SparkContext configuration, which no Connect RPC can 
write, so this path used only the operator's settings, as 
`RejectRequestedRepositories` and the client-side resolution of explicit 
repositories intend.
   
   This affects every Connect server session: the server accepts 
`MAVEN_DEPENDENCY` entries from any client and advertises 
`serverSideMavenArtifacts.v1` unconditionally. Classic 
`SparkSession.addArtifact` is not affected, because its caller already controls 
the driver. For scale, SQL `ADD JAR` with an `ivy://` URI and a `repos` 
parameter already lets a Connect client name repositories on the base branch, 
so this breaks the server-side Maven path's own guarantee rather than a 
server-wide boundary. `AddArtifactsHandlerSuite` sets Ivy settings only in the 
driver SparkConf, so no test covers a Connect session value that differs from 
the driver's.
   
   **Recommended change:** Make server-side Spark Connect Maven resolution read 
spark.jars.ivySettings, spark.jars.ivy and both Ivy timeouts, including any 
${...} expansion, from the SparkContext configuration instead of the 
client-writable session configuration. Note in the docs for these settings that 
server-side Spark Connect resolution uses the server's configuration. Add Spark 
Connect AddArtifacts handler tests in which a client-set session value differs 
from the driver's.
   
   **Why this works:** The server-side resolution path used by the AddArtifacts 
handler gets its own lazily built resolver and timeout values sourced from the 
SparkContext configuration. It reuses the session's .ivy-cache directory, and 
the handler keeps capping those timeouts by the request deadline. Classic 
SparkSession.addArtifact and addArtifacts keep the session-sourced resolver and 
timeouts. Server-side values are read and expanded only against the 
SparkContext configuration, so neither a client-written core key nor a 
client-written substitution key can influence server-side resolution.
   
   **Scope:** Keep server-side Spark Connect Maven resolution on the server's 
own Ivy settings, while classic SparkSession.addArtifact keeps the new 
session-level settings.
   
   **Compatibility:** Classic SparkSession.addArtifact and addArtifacts keep 
reading Ivy settings from the owning session. Server-side Spark Connect 
resolution keeps its request-deadline capping, its rejection of URI-requested 
repositories, and its session-local Ivy cache.
   
   **Risks:** With this repair, individual Spark Connect sessions cannot use 
their own Ivy settings for server-side resolution. As the pull request stands, 
a client can do so only after disabling an internal legacy flag, and the base 
branch used only the server's settings, so no supported Spark Connect behavior 
is removed.
   
   **Constraints:** Classic SparkSession.addArtifact and addArtifacts must keep 
honoring session-specific Ivy settings, substitution and timeouts as this 
change intends. Server-side resolution must keep using the session's own 
.ivy-cache directory, keep capping its timeouts by the request deadline, and 
keep rejecting repositories requested in the Ivy URI.
   
   **Exclusions:**
   - Client-side Spark Connect Ivy resolution and the routing of Ivy URIs 
between client and server, which this change leaves to separate work.
   - SQL ADD JAR with an ivy:// URI, which resolves on the driver with 
repositories named in the URI and with settings from JVM system properties. The 
pull request does not change it, and this repair leaves it as it is.
   
   **Success:** On a Spark Connect server, a server-side Maven dependency 
resolves with the driver's spark.jars.ivySettings, spark.jars.ivy and timeouts, 
even after the client disables spark.sql.legacy.setCommandRejectsSparkCoreConfs 
and writes different values or substitution keys into its session. Classic 
SparkSession.addArtifact and addArtifacts still resolve with session-specific 
Ivy settings. The documentation for these settings says that server-side Spark 
Connect resolution uses the server's configuration.
   
   **Verification:**
   - **Regression:** A server-side Maven dependency sent after the client 
disables spark.sql.legacy.setCommandRejectsSparkCoreConfs and sets 
spark.jars.ivySettings in its session to an empty string or to a different 
settings file still resolves only through the repository the driver configures.
   - **Regression:** A client-set session value of spark.jars.ivy, written 
after the client disables spark.sql.legacy.setCommandRejectsSparkCoreConfs, 
does not change the local repository root that server-side resolution searches.
   - **Regression:** When the driver's spark.jars.ivySettings uses a ${spark.*} 
reference to an unregistered key, a client-set session value for that key does 
not change the settings file that server-side resolution loads.
   - **Regression:** Client-set session values of spark.jars.ivyConnectTimeout 
and spark.jars.ivyReadTimeout do not change the timeouts used by server-side 
resolution, which stay at the driver values capped by the request deadline.
   - **Compatibility:** Classic SparkSession.addArtifact and addArtifacts 
continue to honor session-specific Ivy settings, substitution and timeouts, and 
keep downloaded jars and the Ivy cache session-local.



##########
core/src/main/scala/org/apache/spark/internal/config/package.scala:
##########
@@ -3085,6 +3087,7 @@ package object config {
       .doc("Path to an Ivy settings file to customize resolution of jars 
specified " +
         "using spark.jars.packages or ivy:// URIs passed to 
SparkSession.addArtifact instead " +
         "of the built-in defaults, such as maven central. " +
+        "SparkSession.addArtifact reads this setting from the session 
configuration. " +

Review Comment:
   **Nit (P3):** This says `addArtifact` reads the setting from the session 
configuration, but `ArtifactManager` captures `spark.jars.ivySettings`, and 
likewise `spark.jars.ivy`, only once: when the lazy `runtimeDependencyResolver` 
is built on the session's first `ivy://` resolution. It never re-reads them. 
For example, after `spark.addArtifact("ivy://g:a:1")`, 
`SparkSession.builder().config("spark.jars.ivySettings", 
"/opt/mirror/ivysettings.xml").getOrCreate()` updates the session's value, and 
`spark.conf.get` reports it, yet the next `ivy://` artifact still resolves with 
the first settings file. The builder logs its generic "only runtime SQL 
configurations will take effect" warning whether or not the value takes effect, 
so nothing identifies this case.
   
   The timeout entries do document a capture point, but theirs is different: 
the timeouts are captured on the first path- or URI-based 
`addArtifact`/`addArtifacts` call of any scheme. So after 
`spark.addArtifact(localJar)`, a later timeout change is ignored while a later 
settings change still applies. Every new test writes its values on a fresh 
session before first use, so neither capture point is pinned: snapshotting at 
construction or re-reading on every call would also pass.
   
   **Verification:**
   - **Behavior:** A session value of spark.jars.ivySettings or spark.jars.ivy 
written after the session's artifact manager exists but before its first ivy:// 
resolution is used by that resolution.
   - **Behavior:** A session value written after the session's first ivy:// 
resolution is not used by later resolutions in that session, and timeouts 
captured by a first non-Ivy addArtifact call are not changed by a later session 
write.
   - **Inspection:** The four config entries' doc strings and the matching rows 
of the configuration documentation state the same capture points.



-- 
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