HyukjinKwon commented on code in PR #59320:
URL: https://github.com/apache/spark/pull/59320#discussion_r4271696331


##########
sql/core/src/main/scala/org/apache/spark/sql/classic/RuntimeConfig.scala:
##########
@@ -46,8 +46,8 @@ class RuntimeConfig private[sql](val sqlConf: SQLConf = new 
SQLConf) extends sql
       // Connect config RPC writes through this method too, so both front ends 
behave the same way.
       //
       // Scoped to the prefix so an ordinary configuration write does not take 
the monitor. Check
-      // and write under the monitor that guards the session configurations -- 
`settings` is a
-      // `Collections.synchronizedMap`, so every `put` locks the wrapper this 
block locks -- because
+      // and write under the monitor that guards the session configurations -- 
every method of

Review Comment:
   **Nit (P3):** This now says every method of `settings` locks the map itself, 
but `ModificationCountingMap`'s `keySet`, `values`, and `entrySet` return 
unsynchronized views. Its class doc and `ModificationCountingMapSuite` ("every 
method except the views locks the map itself") both carve them out. The block 
itself is still correct, because `put` goes through `mutate`, which 
synchronizes on the map. The old wording was accurate because 
`Collections.synchronizedMap` views lock the same mutex. The new wording could 
lead someone to call, say, `sqlConf.settings.keySet.contains(k)` without 
holding the monitor and race with a concurrent `SET`. Could it be narrowed to 
something like "every method of `settings` except its views locks the map 
itself, so every `put` locks the map this block locks"?
   
   **Verification:**
   - **Inspection:** The revised comment's locking claim matches 
ModificationCountingMap: non-view methods (including put) synchronize on the 
map itself, and keySet/values/entrySet are excluded from that claim.



##########
sql/catalyst/benchmarks/RetainedResolutionConfigsBenchmark-results.txt:
##########
@@ -0,0 +1,382 @@
+================================================================================================
+view: 32 settings, 25% retained, warm
+================================================================================================
+
+OpenJDK 64-Bit Server VM 17.0.15+6-Ubuntu-0ubuntu120.04 on Linux 
5.4.0-1163-aws-fips

Review Comment:
   Thanks, verified: all three result files now come from the "Run benchmarks" 
workflow runner (AMD EPYC 7763, Linux 6.17.0-1022-azure) for JDK 17, 21, and 
25, consistent with the other catalyst benchmark results.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4258683487","thread_id":"inline:4258683487","verdict_sha256":"ae877c56c9e26aa21afebfcdd9e33ffa742e923b49b144b08d69c09e8df9705d"}
 -->



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala:
##########
@@ -326,15 +326,30 @@ object Analyzer {
       newConf: SQLConf,
       existingConf: SQLConf,
       createSparkVersion: String = ""): Unit = {
-    existingConf.getAllConfs.foreach { case (key, value) =>
-      if (key.startsWith("spark.sql.catalog.") || 
SQLConf.isSessionBindingPolicy(key)) {
-        newConf.settings.put(key, value)
+    // The retained configs are memoized on `existingConf` unless
+    // `ANALYZER_MEMOIZE_RETAINED_RESOLUTION_CONFIGS` is off, which recomputes 
them on every build.
+    if 
(existingConf.getConf(SQLConf.ANALYZER_MEMOIZE_RETAINED_RESOLUTION_CONFIGS)) {
+      newConf.settings.putAll(existingConf.retainedResolutionConfigs)
+    } else {
+      existingConf.getAllConfs.foreach { case (key, value) =>
+        if (bodyFollowsCaller(key)) {
+          newConf.settings.put(key, value)
+        }
       }
     }
 
     trySetAnsiValue(newConf, createSparkVersion)
   }
 
+  /**
+   * True if a view or SQL function body takes the caller's value for `key` 
rather than the

Review Comment:
   Thanks, verified: the narrowed doc now says only the caller's explicitly set 
keys replace saved values, and only on paths that go through 
`retainResolutionConfigsForAnalysis`.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4258683496","thread_id":"inline:4258683496","verdict_sha256":"ae877c56c9e26aa21afebfcdd9e33ffa742e923b49b144b08d69c09e8df9705d"}
 -->



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala:
##########
@@ -2893,6 +2935,19 @@ object SQLConf {
       .booleanConf
       .createWithDefault(true)
 
+  val ANALYZER_MEMOIZE_RETAINED_RESOLUTION_CONFIGS =
+    buildConf("spark.sql.analyzer.memoizeRetainedResolutionConfigs.enabled")
+      .internal()
+      .version("4.4.0")
+      .withBindingPolicy(ConfigBindingPolicy.SESSION)

Review Comment:
   Thanks, verified: the flag is now `NOT_APPLICABLE`, and the on/off 
equivalence tests still expect an explicitly set value to follow into body 
confs.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4258683499","thread_id":"inline:4258683499","verdict_sha256":"ae877c56c9e26aa21afebfcdd9e33ffa742e923b49b144b08d69c09e8df9705d"}
 -->



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/ModificationCountingMap.scala:
##########
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.spark.sql.internal
+
+import java.util
+import java.util.function.{BiConsumer, BiFunction, Function => JFunction}
+
+/**
+ * A thread-safe map of [[SQLConf]] settings that counts the mutating calls on 
it, so values
+ * derived from its entries can be reused while [[modificationCount]] is 
unchanged.
+ *
+ * Like the `java.util.Collections.synchronizedMap` it replaces, every method 
of the map
+ * synchronizes on the map itself, so a caller iterating the map must hold its 
monitor. Unlike it,
+ * the key, value, and entry views are read-only, so every mutation goes 
through a method that
+ * advances the count, and unsynchronized, so a caller must hold the monitor 
while using them.
+ */
+private[sql] final class ModificationCountingMap extends util.Map[String, 
String] {
+
+  private val underlying = new util.HashMap[String, String]
+
+  private val readOnlyUnderlying = util.Collections.unmodifiableMap[String, 
String](underlying)
+
+  @volatile private var mutations = 0L
+
+  /**
+   * Number of mutating calls so far; a call may advance it without changing 
the entries. Values
+   * derived from the entries stay valid while it is unchanged; use
+   * [[snapshotWithModificationCount]] to pair a count with the entries it 
describes.
+   */
+  def modificationCount: Long = mutations
+
+  /** A copy of the entries and the [[modificationCount]] they correspond to. 
*/
+  def snapshotWithModificationCount: (Long, util.Map[String, String]) = 
synchronized {
+    (mutations, new util.HashMap[String, String](underlying))
+  }
+
+  private def mutate[T](f: => T): T = synchronized {

Review Comment:
   Thanks, verified: the new test blocks every mutator, 
`snapshotWithModificationCount`, and each read on the map's own monitor, so 
moving any of them to a private lock or removing the lock would fail it.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4258683512","thread_id":"inline:4258683512","verdict_sha256":"ae877c56c9e26aa21afebfcdd9e33ffa742e923b49b144b08d69c09e8df9705d"}
 -->



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