From adcae0cd8f305195f0cb98a79f42c0099890d763 Mon Sep 17 00:00:00 2001 From: Cheng Pan Date: Fri, 21 Aug 2026 19:46:47 +0800 Subject: [PATCH 1/2] Cache HiveResult reflection methods --- .../kyuubi/engine/spark/schema/RowSet.scala | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/schema/RowSet.scala b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/schema/RowSet.scala index 47e6351b2d9..250486e1465 100644 --- a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/schema/RowSet.scala +++ b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/schema/RowSet.scala @@ -30,18 +30,16 @@ object RowSet { // SPARK-47911 (4.0.0) introduced it type BinaryFormatter = Array[Byte] => String - def getBinaryFormatter: BinaryFormatter = + private val getBinaryFormatterMethod: DynMethods.BoundMethod = DynMethods.builder("getBinaryFormatter") .impl(HiveResult.getClass) // for Spark 4.0 and later .orNoop() // for Spark 3.5 and before .buildChecked(HiveResult) - .invokeChecked[BinaryFormatter]() - def toHiveString( - valueAndType: (Any, DataType), - nested: JBoolean = false, - timeFormatters: TimeFormatters, - binaryFormatter: BinaryFormatter): String = + def getBinaryFormatter: BinaryFormatter = + getBinaryFormatterMethod.invokeChecked[BinaryFormatter]() + + private val toHiveStringMethod: DynMethods.BoundMethod = DynMethods.builder("toHiveString") .impl( // for Spark 3.5 and before HiveResult.getClass, @@ -55,5 +53,11 @@ object RowSet { classOf[TimeFormatters], classOf[BinaryFormatter]) .buildChecked(HiveResult) - .invokeChecked[String](valueAndType, nested, timeFormatters, binaryFormatter) + + def toHiveString( + valueAndType: (Any, DataType), + nested: JBoolean = false, + timeFormatters: TimeFormatters, + binaryFormatter: BinaryFormatter): String = + toHiveStringMethod.invokeChecked[String](valueAndType, nested, timeFormatters, binaryFormatter) } From cc5bafc6062626b8a7dc970048b2dfcda9fb0f16 Mon Sep 17 00:00:00 2001 From: Cheng Pan Date: Mon, 24 Aug 2026 15:16:17 +0800 Subject: [PATCH 2/2] Cache Spark reflection methods --- .../kyuubi/engine/spark/KyuubiSparkUtil.scala | 52 +++++++--------- .../org/apache/spark/ui/SparkUIUtils.scala | 62 ++++++++++--------- 2 files changed, 56 insertions(+), 58 deletions(-) diff --git a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/KyuubiSparkUtil.scala b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/KyuubiSparkUtil.scala index 8c19ea5c252..67682443383 100644 --- a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/KyuubiSparkUtil.scala +++ b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/KyuubiSparkUtil.scala @@ -131,37 +131,31 @@ object KyuubiSparkUtil extends Logging { // jakarta.ws.rs, this is an equivalent implementation using reflection of the following // plain invocation: // {javax|jakarta}.ws.rs.core.UriBuilder.fromUri(uri).fragment(fragment).build() - def buildURI(uri: URI, fragment: String): URI = { + private lazy val uriBuilderClassName: String = if (SPARK_ENGINE_RUNTIME_VERSION >= "4.0") { - var uriBuilder = DynMethods.builder("fromUri") - .impl("jakarta.ws.rs.core.UriBuilder", classOf[URI]) - .buildStatic() - .invoke[AnyRef](uri) - - uriBuilder = DynMethods.builder("fragment") - .impl("jakarta.ws.rs.core.UriBuilder", classOf[String]) - .build(uriBuilder) - .invoke[AnyRef](fragment) - - DynMethods.builder("build") - .impl("jakarta.ws.rs.core.UriBuilder", classOf[Array[AnyRef]]) - .build(uriBuilder) - .invoke[URI](Array.empty[AnyRef]) + "jakarta.ws.rs.core.UriBuilder" } else { - var uriBuilder = DynMethods.builder("fromUri") - .impl("javax.ws.rs.core.UriBuilder", classOf[URI]) - .buildStatic() - .invoke[AnyRef](uri) - - uriBuilder = DynMethods.builder("fragment") - .impl("javax.ws.rs.core.UriBuilder", classOf[String]) - .build(uriBuilder) - .invoke[AnyRef](fragment) - - DynMethods.builder("build") - .impl("javax.ws.rs.core.UriBuilder", classOf[Array[AnyRef]]) - .build(uriBuilder) - .invoke[URI](Array.empty[AnyRef]) + "javax.ws.rs.core.UriBuilder" } + + private lazy val fromUriMethod: DynMethods.StaticMethod = + DynMethods.builder("fromUri") + .impl(uriBuilderClassName, classOf[URI]) + .buildStaticChecked() + + private lazy val fragmentMethod: DynMethods.UnboundMethod = + DynMethods.builder("fragment") + .impl(uriBuilderClassName, classOf[String]) + .buildChecked() + + private lazy val buildMethod: DynMethods.UnboundMethod = + DynMethods.builder("build") + .impl(uriBuilderClassName, classOf[Array[AnyRef]]) + .buildChecked() + + def buildURI(uri: URI, fragment: String): URI = { + val uriBuilder = fromUriMethod.invoke[AnyRef](uri) + val fragmentedUriBuilder = fragmentMethod.invoke[AnyRef](uriBuilder, fragment) + buildMethod.invoke[URI](fragmentedUriBuilder, Array.empty[AnyRef]) } } diff --git a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/ui/SparkUIUtils.scala b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/ui/SparkUIUtils.scala index fec10606ce0..0b04c8c7010 100644 --- a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/ui/SparkUIUtils.scala +++ b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/ui/SparkUIUtils.scala @@ -30,20 +30,8 @@ import org.apache.kyuubi.util.reflect.DynMethods */ object SparkUIUtils { - def formatDuration(ms: Long): String = { - UIUtils.formatDuration(ms) - } - - def headerSparkPage( - request: HttpServletRequestLike, - title: String, - content: Seq[Node], - activeTab: SparkUITab, - helpText: Option[String] = None, - showVisualization: JBoolean = false, - useDataTables: JBoolean = false, - useTimeline: JBoolean = false): Seq[Node] = { - val headerSparkPageMethod = if (SPARK_ENGINE_RUNTIME_VERSION >= "4.0") { + private val headerSparkPageMethod: DynMethods.BoundMethod = + if (SPARK_ENGINE_RUNTIME_VERSION >= "4.0") { DynMethods.builder("headerSparkPage") // SPARK-56354 (4.2.0) added the useTimeline flag as an 8th parameter .impl( @@ -79,22 +67,9 @@ object SparkUIUtils { classOf[Boolean]) .buildChecked(UIUtils) } - headerSparkPageMethod.invoke[Seq[Node]]( - request.underlying, - title, - () => content, - activeTab, - helpText, - showVisualization, - useDataTables, - useTimeline) - } - def prependBaseUri( - request: HttpServletRequestLike, - basePath: String = "", - resource: String = ""): String = { - val prependBaseUriMethod = if (SPARK_ENGINE_RUNTIME_VERSION >= "4.0") { + private val prependBaseUriMethod: DynMethods.BoundMethod = + if (SPARK_ENGINE_RUNTIME_VERSION >= "4.0") { DynMethods.builder("prependBaseUri") .impl( UIUtils.getClass, @@ -111,6 +86,35 @@ object SparkUIUtils { classOf[String]) .buildChecked(UIUtils) } + + def formatDuration(ms: Long): String = { + UIUtils.formatDuration(ms) + } + + def headerSparkPage( + request: HttpServletRequestLike, + title: String, + content: Seq[Node], + activeTab: SparkUITab, + helpText: Option[String] = None, + showVisualization: JBoolean = false, + useDataTables: JBoolean = false, + useTimeline: JBoolean = false): Seq[Node] = { + headerSparkPageMethod.invoke[Seq[Node]]( + request.underlying, + title, + () => content, + activeTab, + helpText, + showVisualization, + useDataTables, + useTimeline) + } + + def prependBaseUri( + request: HttpServletRequestLike, + basePath: String = "", + resource: String = ""): String = { prependBaseUriMethod.invoke[String](request.underlying, basePath, resource) } }