This is an automated email from the ASF dual-hosted git repository.

chengpan pushed a commit to branch branch-1.11
in repository https://gitbox.apache.org/repos/asf/kyuubi.git


The following commit(s) were added to refs/heads/branch-1.11 by this push:
     new 6e3877fb9c [KYUUBI #7304] Fix NumberFormatException when parsing ISO 
8601 duration for session idle timeout
6e3877fb9c is described below

commit 6e3877fb9c79a579d439dad832327af37a1077a7
Author: anandnalya <[email protected]>
AuthorDate: Thu Jan 15 18:53:02 2026 +0800

    [KYUUBI #7304] Fix NumberFormatException when parsing ISO 8601 duration for 
session idle timeout
    
    The `sessionIdleTimeoutThreshold` in `SparkSessionImpl` was using `.toLong` 
directly on the config string value, which fails when the value is in ISO 8601 
duration format (e.g., `PT40M`). This change uses `Duration.parse()` to 
properly handle ISO 8601 duration format with fallback to plain milliseconds, 
consistent with how `ConfigBuilder.timeConf` handles duration parsing elsewhere 
in the codebase.
    
    Closes #7304
    
    ### Why are the changes needed?
    
    This fixes https://github.com/apache/kyuubi/issues/7304 which affects 
v1.11.0 and v1.10.3
    
    ### How was this patch tested?
    
    Tested with integration tests.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Integration tests Generated-by: Claude Opus 4.5
    
    Closes #7306 from anandnalya/fix/session-idle-timeout-duration-parsing.
    
    Closes #7304
    
    3bb8891e0 [anandnalya] [KYUUBI #7304] Fix NumberFormatException when 
parsing ISO 8601 duration for session idle timeout
    
    Authored-by: anandnalya <[email protected]>
    Signed-off-by: Cheng Pan <[email protected]>
    (cherry picked from commit 69e8e95429b5bafb9eae1bff15cbaf1636756395)
    Signed-off-by: Cheng Pan <[email protected]>
---
 .../engine/spark/session/SparkSessionImpl.scala    |  9 +-
 .../spark/session/SessionIdleTimeoutSuite.scala    | 99 ++++++++++++++++++++++
 2 files changed, 105 insertions(+), 3 deletions(-)

diff --git 
a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/session/SparkSessionImpl.scala
 
b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/session/SparkSessionImpl.scala
index 9498483be6..829fba2186 100644
--- 
a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/session/SparkSessionImpl.scala
+++ 
b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/session/SparkSessionImpl.scala
@@ -17,8 +17,11 @@
 
 package org.apache.kyuubi.engine.spark.session
 
+import java.time.Duration
 import java.util.concurrent.atomic.AtomicLong
 
+import scala.util.Try
+
 import org.apache.commons.lang3.StringUtils
 import org.apache.spark.sql.{AnalysisException, SparkSession}
 import org.apache.spark.ui.SparkUIUtils.formatDuration
@@ -60,9 +63,9 @@ class SparkSessionImpl(
 
   override val sessionIdleTimeoutThreshold: Long = {
     conf.get(SESSION_IDLE_TIMEOUT.key)
-      .map(_.toLong)
-      .getOrElse(
-        sessionManager.getConf.get(SESSION_IDLE_TIMEOUT))
+      .map(_.trim)
+      .map(v => Try(Duration.parse(v).toMillis).getOrElse(v.toLong))
+      .getOrElse(sessionManager.getConf.get(SESSION_IDLE_TIMEOUT))
   }
 
   private val sessionEvent = SessionEvent(this)
diff --git 
a/externals/kyuubi-spark-sql-engine/src/test/scala/org/apache/kyuubi/engine/spark/session/SessionIdleTimeoutSuite.scala
 
b/externals/kyuubi-spark-sql-engine/src/test/scala/org/apache/kyuubi/engine/spark/session/SessionIdleTimeoutSuite.scala
new file mode 100644
index 0000000000..969c9ed28a
--- /dev/null
+++ 
b/externals/kyuubi-spark-sql-engine/src/test/scala/org/apache/kyuubi/engine/spark/session/SessionIdleTimeoutSuite.scala
@@ -0,0 +1,99 @@
+/*
+ * 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.kyuubi.engine.spark.session
+
+import java.sql.DriverManager
+
+import org.apache.kyuubi.config.KyuubiConf._
+import org.apache.kyuubi.engine.spark.WithSparkSQLEngine
+
+class SessionIdleTimeoutSuite extends WithSparkSQLEngine {
+
+  // Load JDBC driver
+  Class.forName("org.apache.kyuubi.jdbc.KyuubiHiveDriver")
+
+  override def withKyuubiConf: Map[String, String] = {
+    Map(
+      ENGINE_SHARE_LEVEL.key -> "SERVER",
+      ENGINE_SPARK_MAX_INITIAL_WAIT.key -> "0")
+  }
+
+  private def baseJdbcUrl: String =
+    s"jdbc:hive2://${engine.frontendServices.head.connectionUrl}/default"
+
+  test("KYUUBI #7304: session idle timeout supports ISO 8601 duration format") 
{
+    // Test with ISO 8601 duration format (PT40M = 40 minutes = 2400000 ms)
+    val jdbcUrlWithIso8601Timeout =
+      s"$baseJdbcUrl;#${SESSION_IDLE_TIMEOUT.key}=PT40M"
+    val conn = DriverManager.getConnection(jdbcUrlWithIso8601Timeout, 
"anonymous", "")
+    try {
+      val statement = conn.createStatement()
+      try {
+        val resultSet = statement.executeQuery("SELECT 1")
+        assert(resultSet.next())
+        assert(resultSet.getInt(1) == 1)
+      } finally {
+        statement.close()
+      }
+    } finally {
+      conn.close()
+    }
+  }
+
+  test("KYUUBI #7304: session idle timeout supports plain milliseconds") {
+    // Test with plain milliseconds (2400000 ms = 40 minutes)
+    val jdbcUrlWithMillisTimeout =
+      s"$baseJdbcUrl;#${SESSION_IDLE_TIMEOUT.key}=2400000"
+    val conn = DriverManager.getConnection(jdbcUrlWithMillisTimeout, 
"anonymous", "")
+    try {
+      val statement = conn.createStatement()
+      try {
+        val resultSet = statement.executeQuery("SELECT 1")
+        assert(resultSet.next())
+        assert(resultSet.getInt(1) == 1)
+      } finally {
+        statement.close()
+      }
+    } finally {
+      conn.close()
+    }
+  }
+
+  test("KYUUBI #7304: session idle timeout supports various ISO 8601 formats") 
{
+    // Test with different ISO 8601 duration formats
+    val testCases = Seq("PT1H", "PT30M", "PT1H30M", "PT90S")
+
+    testCases.foreach { durationStr =>
+      val jdbcUrlWithTimeout =
+        s"$baseJdbcUrl;#${SESSION_IDLE_TIMEOUT.key}=$durationStr"
+      val conn = DriverManager.getConnection(jdbcUrlWithTimeout, "anonymous", 
"")
+      try {
+        val statement = conn.createStatement()
+        try {
+          val resultSet = statement.executeQuery("SELECT 1")
+          assert(resultSet.next())
+          assert(resultSet.getInt(1) == 1)
+        } finally {
+          statement.close()
+        }
+      } finally {
+        conn.close()
+      }
+    }
+  }
+}

Reply via email to