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


##########
common/utils/src/main/scala/org/apache/spark/internal/Logging.scala:
##########
@@ -96,10 +96,10 @@ class LogEntry(messageWithContext: => MessageWithContext) {
 
   def context: java.util.Map[String, String] = cachedMessageWithContext.context
 
-  def +(other: LogEntry): LogEntry = {
-    val combined = cachedMessageWithContext + other.cachedMessageWithContext
-    new LogEntry(combined)
-  }
+  def +(other: LogEntry): LogEntry =
+    // Concatenation passed by name to LogEntry constructor, so that neither 
entry is evaluated
+    // unless the result is logged.
+    new LogEntry(cachedMessageWithContext + other.cachedMessageWithContext)

Review Comment:
   **Nit (P3):** `SQLLastAttemptAccumulator.logAccumulatorState` 
(`sql/core/.../metric/SQLLastAttemptAccumulator.scala:221-234`) wraps 
`super.logAccumulatorState + log"""..."""` in a `try` whose `NonFatal` handler 
logs a warning and returns `log"<Unexpected exception in 
logAccumulatorState>"`. With the old eager `+`, that concatenation ran the base 
class's state formatting (`value`, `lastAttemptDirectDriverValue`, 
`lastAttemptRddsMap`, ...) inside the `try`, so a formatting failure became the 
placeholder. With this change the `try` only builds a thunk. The formatting now 
runs later, when `logWarning(logEntry)` in 
`LastAttemptAccumulator.unexpectedLastAttemptMetricUpdate` reads the entry, 
outside the `catch`.
   
   If that formatting throws, the exception escapes before 
`lastAttemptAccumulatorInvalid = true` is set. On the `mergeLastAttempt` path 
it ends up in `DAGScheduler.updateAccumulators`' error handler with the 
accumulator still marked valid. On the `setValueIfOnDriverSide` path it can 
propagate out of the driver-side metric update. The base class's own 
`try`/`catch` never covered its interpolation, because the by-name 
`LogEntry.from` conversion is applied inside the `try` body, so only the SQL 
override loses protection here.
   
   I couldn't find a concrete input that makes the formatting throw, so this 
should be rare. Since the guard is deliberate, it may be worth keeping it 
effective by moving it into the by-name body, so it runs when the entry is 
evaluated and `+` stays lazy. For example:
   
   ```scala
   override def logAccumulatorState: LogEntry = {
     val base = super.logAccumulatorState
     LogEntry.from {
       try {
         val driverQEVals = ...
         val entry = base + log"""...""".stripMargin
         MessageWithContext(entry.message, entry.context)
       } catch {
         case NonFatal(e) =>
           logWarning(log"Unexpected exception in logAccumulatorState", e)
           log"<Unexpected exception in logAccumulatorState>"
       }
     }
   }
   ```
   
   **Verification:**
   - **Regression:** With an SQL last-attempt accumulator whose base state 
formatting throws NonFatal, reading the logAccumulatorState entry yields the 
placeholder text and does not throw. The merge-target behavior with this PR's 
lazy `+` would fail this check.
   - **Behavior:** For a well-formed SQL accumulator, the logAccumulatorState 
entry still contains the base state and the 'Direct driver QE values' section 
once read.
   - **Compile:** sql/core main and test sources compile with the reworked 
override.



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