Skip to content

Commit 69e8e95

Browse files
anandnalyapan3793
authored andcommitted
[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 #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 3bb8891 [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]>
1 parent b49ef79 commit 69e8e95

File tree

2 files changed

+105
-3
lines changed

2 files changed

+105
-3
lines changed

externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/session/SparkSessionImpl.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
package org.apache.kyuubi.engine.spark.session
1919

20+
import java.time.Duration
2021
import java.util.concurrent.atomic.AtomicLong
2122

23+
import scala.util.Try
24+
2225
import org.apache.commons.lang3.StringUtils
2326
import org.apache.spark.sql.{AnalysisException, SparkSession}
2427
import org.apache.spark.ui.SparkUIUtils.formatDuration
@@ -60,9 +63,9 @@ class SparkSessionImpl(
6063

6164
override val sessionIdleTimeoutThreshold: Long = {
6265
conf.get(SESSION_IDLE_TIMEOUT.key)
63-
.map(_.toLong)
64-
.getOrElse(
65-
sessionManager.getConf.get(SESSION_IDLE_TIMEOUT))
66+
.map(_.trim)
67+
.map(v => Try(Duration.parse(v).toMillis).getOrElse(v.toLong))
68+
.getOrElse(sessionManager.getConf.get(SESSION_IDLE_TIMEOUT))
6669
}
6770

6871
private val sessionEvent = SessionEvent(this)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.kyuubi.engine.spark.session
19+
20+
import java.sql.DriverManager
21+
22+
import org.apache.kyuubi.config.KyuubiConf._
23+
import org.apache.kyuubi.engine.spark.WithSparkSQLEngine
24+
25+
class SessionIdleTimeoutSuite extends WithSparkSQLEngine {
26+
27+
// Load JDBC driver
28+
Class.forName("org.apache.kyuubi.jdbc.KyuubiHiveDriver")
29+
30+
override def withKyuubiConf: Map[String, String] = {
31+
Map(
32+
ENGINE_SHARE_LEVEL.key -> "SERVER",
33+
ENGINE_SPARK_MAX_INITIAL_WAIT.key -> "0")
34+
}
35+
36+
private def baseJdbcUrl: String =
37+
s"jdbc:hive2://${engine.frontendServices.head.connectionUrl}/default"
38+
39+
test("KYUUBI #7304: session idle timeout supports ISO 8601 duration format") {
40+
// Test with ISO 8601 duration format (PT40M = 40 minutes = 2400000 ms)
41+
val jdbcUrlWithIso8601Timeout =
42+
s"$baseJdbcUrl;#${SESSION_IDLE_TIMEOUT.key}=PT40M"
43+
val conn = DriverManager.getConnection(jdbcUrlWithIso8601Timeout, "anonymous", "")
44+
try {
45+
val statement = conn.createStatement()
46+
try {
47+
val resultSet = statement.executeQuery("SELECT 1")
48+
assert(resultSet.next())
49+
assert(resultSet.getInt(1) == 1)
50+
} finally {
51+
statement.close()
52+
}
53+
} finally {
54+
conn.close()
55+
}
56+
}
57+
58+
test("KYUUBI #7304: session idle timeout supports plain milliseconds") {
59+
// Test with plain milliseconds (2400000 ms = 40 minutes)
60+
val jdbcUrlWithMillisTimeout =
61+
s"$baseJdbcUrl;#${SESSION_IDLE_TIMEOUT.key}=2400000"
62+
val conn = DriverManager.getConnection(jdbcUrlWithMillisTimeout, "anonymous", "")
63+
try {
64+
val statement = conn.createStatement()
65+
try {
66+
val resultSet = statement.executeQuery("SELECT 1")
67+
assert(resultSet.next())
68+
assert(resultSet.getInt(1) == 1)
69+
} finally {
70+
statement.close()
71+
}
72+
} finally {
73+
conn.close()
74+
}
75+
}
76+
77+
test("KYUUBI #7304: session idle timeout supports various ISO 8601 formats") {
78+
// Test with different ISO 8601 duration formats
79+
val testCases = Seq("PT1H", "PT30M", "PT1H30M", "PT90S")
80+
81+
testCases.foreach { durationStr =>
82+
val jdbcUrlWithTimeout =
83+
s"$baseJdbcUrl;#${SESSION_IDLE_TIMEOUT.key}=$durationStr"
84+
val conn = DriverManager.getConnection(jdbcUrlWithTimeout, "anonymous", "")
85+
try {
86+
val statement = conn.createStatement()
87+
try {
88+
val resultSet = statement.executeQuery("SELECT 1")
89+
assert(resultSet.next())
90+
assert(resultSet.getInt(1) == 1)
91+
} finally {
92+
statement.close()
93+
}
94+
} finally {
95+
conn.close()
96+
}
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)