andygrove opened a new issue, #5346:
URL: https://github.com/apache/datafusion-comet/issues/5346

   ## What is the problem the feature request solves?
   
   Fallback reasons are currently recorded as pre-formatted strings. 
`withFallbackReason` / `withFallbackReasons` accumulate a `Set[String]` on the 
`CometExplainInfo.FALLBACK_REASONS` tag, so every call site decides the final 
user-facing wording at tag time and the only thing downstream consumers 
(extended explain, the fallback log, tests) can do with a reason is print it or 
substring-match it.
   
   There are ~205 `withFallbackReason` call sites today, and the string 
representation is causing several concrete problems:
   
   **1. Tests substring-match on message wording.** 
`CometTestBase.checkSparkAnswerAndFallbackReasons` asserts with 
`actualFallbacks.exists(_.contains(reason))`. This makes the exact phrasing of 
every message a de-facto public API — rewording one message breaks tests that 
have nothing to do with the change — while simultaneously being loose enough to 
pass accidentally on an unrelated reason that happens to contain the substring.
   
   **2. Deduplication happens on rendered text, not on the reason.** The tag is 
a `Set[String]` and most messages interpolate expression or data type text, so 
two occurrences of the same underlying reason that render slightly differently 
become two distinct set members and are both reported.
   
   **3. Phrasing has already drifted.** At least five spellings of a single 
reason exist in the codebase:
   
   ```
   Unsupported data type
   Unsupported data type: ${expr.dataType}
   Unsupported datatype $dataType
   Unsupported datatype in castToProto: $dt
   Failed to serialize datatype ${expr.dataType} for scalar subquery
   ```
   
   The same is true of the "a config is disabled" family (`X is not enabled`, 
`requires X to be enabled`, `Set X`, `Expression support is disabled. Set 
...`), where some variants include the "how to fix it" hint and some do not. 
`NativeOptIn.message` and `SupportLevel.strictFloatingPointReason` already 
exist specifically to hand-roll shared wording for two of these families, which 
suggests the general mechanism is missing.
   
   **4. A newline-splitting hack exists only because reasons are strings.** 
`withFallbackReason` splits its argument on `"\n"` to support call sites that 
pack several reasons into one string.
   
   **5. Reasons are not queryable.** The only programmatic signal available is 
`hasFallbackReason`, a boolean. Nothing can distinguish "fell back because a 
config is disabled" from "fell back because of a genuine gap in Comet", which 
is information wanted by `spark.comet.strictFallbackReasons`, by coverage 
reporting, and by anything that wants to aggregate the top real gaps across a 
benchmark suite without regexing explain output.
   
   ## Describe the potential solution
   
   Tag with a raw, structured reason and move formatting to the point of 
consumption (extended explain and the fallback log). A sealed hierarchy 
mirroring the existing `SupportLevel` pattern:
   
   ```scala
   sealed trait FallbackReason { def message: String }  // default rendering
   
   object FallbackReason {
     case class ConfigDisabled(conf: ConfigEntry[_]) extends FallbackReason
     case class UnsupportedDataType(dt: DataType, context: Option[String] = 
None) extends FallbackReason
     case class UnsupportedExpression(name: String, notes: Option[String] = 
None) extends FallbackReason
     case class UnsupportedOperator(name: String) extends FallbackReason
     case class NotCompatible(name: String, notes: Option[String], enableConf: 
ConfigEntry[_]) extends FallbackReason
     case class Other(message: String) extends FallbackReason  // escape hatch
   }
   ```
   
   with the tag becoming `TreeNodeTag[Set[FallbackReason]]`.
   
   Two design notes:
   
   - Keep a `message` on the trait rather than pushing all rendering into 
`ExtendedExplainInfo`. The fallback-log path in `withFallbackReasons` needs a 
rendering too, and co-locating the default wording with the case keeps it 
discoverable. Explain can still layer its own decoration on top (grouping, 
truncation, verbose vs. fallback format, appending the config key and 
compatibility-guide link).
   - Store data types and names, not `Expression` instances. Tags live as long 
as the plan does, so holding an `Expression` retains a whole expression 
subtree; render to a name or type at construction.
   
   Structured reasons should also make [issue 
144](https://github.com/apache/datafusion-comet/issues/144) (surface fallback 
reasons in the Spark UI) easier, since a UI wants to group and count reasons 
rather than print a bag of strings.
   
   ## Additional context
   
   This does not need to be a single 205-call-site change. A staged migration 
keeps every intermediate state compiling and rendering identically:
   
   1. Add the hierarchy including the `Other(String)` escape hatch, flip the 
tag to `Set[FallbackReason]`, and keep the existing `withFallbackReason(node, 
s: String)` signature as an overload that wraps in `Other`. Behaviour is 
unchanged at this point.
   2. Convert call sites family by family (config-disabled, unsupported data 
type, unsupported expression/operator), tightening the corresponding tests from 
substring matches to typed matches as each family lands.
   3. Once `Other` is rare, decide whether to remove the string overload or 
keep it for genuine one-offs.
   
   The main thing to be careful about in step 1 is that rendering is preserved 
byte-for-byte, since both the golden extended-explain output and the substring 
assertions in `checkSparkAnswerAndFallbackReasons` depend on the current 
wording.
   


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