This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new 835debdb01 [Cherry-pick to branch-1.3] [#14694] fix(jdbc): prevent
PostgreSQL timeout overflow (#14695) (#14721)
835debdb01 is described below
commit 835debdb0170e5712d149eff802e4bb5cae73197
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Nov 6 19:29:21 2026 +0800
[Cherry-pick to branch-1.3] [#14694] fix(jdbc): prevent PostgreSQL timeout
overflow (#14695) (#14721)
**Cherry-pick Information:**
- Original commit: 9a72e5fe1bfd57f81a299081e19c37f3e7561591
- Target branch: `branch-1.3`
- Status: ✅ Clean cherry-pick (no conflicts)
Co-authored-by: Qi Yu <[email protected]>
---
.../gravitino/catalog/jdbc/config/JdbcConfig.java | 8 ++---
.../catalog/jdbc/utils/JdbcConnectionTimeouts.java | 12 ++++----
.../jdbc/utils/TestJdbcConnectionTimeouts.java | 35 +++++++++++++++-------
docs/jdbc-catalog-connection-validation.md | 4 ++-
4 files changed, 39 insertions(+), 20 deletions(-)
diff --git
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/config/JdbcConfig.java
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/config/JdbcConfig.java
index b9936ca5d5..6d2388ec77 100644
---
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/config/JdbcConfig.java
+++
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/config/JdbcConfig.java
@@ -117,10 +117,10 @@ public class JdbcConfig extends Config {
* otherwise block the connect-only bound forever. An operator value set
directly on the
* connection or in the {@code jdbc-url} is never overridden.
*
- * <p>The value is bounded by {@link Integer#MAX_VALUE} milliseconds because
the MySQL and
- * PostgreSQL driver timeouts are {@code int}s (and the PostgreSQL
millisecond-to-seconds
- * conversion stays within {@code int} range); a larger value would be
accepted here and only fail
- * later when the driver parses it.
+ * <p>The value is bounded by {@link Integer#MAX_VALUE} milliseconds to fit
the drivers' integer
+ * millisecond socket timeouts. PostgreSQL's whole-second timeout is capped
before conversion back
+ * to milliseconds, shortening values near the upper bound by less than one
second to avoid
+ * overflow. Larger millisecond values are rejected here before reaching the
driver.
*/
public static final ConfigEntry<Long> CONNECT_TIMEOUT_MS =
new ConfigBuilder("jdbc.pool.connect-timeout-ms")
diff --git
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/JdbcConnectionTimeouts.java
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/JdbcConnectionTimeouts.java
index 88188551f0..a20d98164d 100644
---
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/JdbcConnectionTimeouts.java
+++
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/JdbcConnectionTimeouts.java
@@ -202,17 +202,19 @@ public final class JdbcConnectionTimeouts {
/**
* Converts milliseconds to seconds, rounded up with a minimum of 1 so a
sub-second bound never
- * collapses to 0 ("no timeout"). The arithmetic is overflow-safe for the
whole positive {@code
- * long} range (the input is validated positive and {@code <=
Integer.MAX_VALUE} by {@link
- * JdbcConfig}).
+ * collapses to 0 ("no timeout"). Cap the result at the largest whole-second
timeout that the
+ * PostgreSQL driver can multiply by 1000 without overflowing its integer
millisecond timeout. The
+ * input is validated positive and {@code <= Integer.MAX_VALUE} by {@link
JdbcConfig}.
*
* @param millis the timeout in milliseconds (positive)
- * @return the timeout in whole seconds, at least 1
+ * @return the timeout in whole seconds, from 1 through {@code
Integer.MAX_VALUE / 1000}
*/
private static long millisToSeconds(long millis) {
// (millis - 1) / 1000 + 1 is ceil(millis/1000) for positive millis
without the overflow that
// (millis + 999) would hit near Long.MAX_VALUE.
- return (millis - 1L) / 1000L + 1L;
+ // PostgreSQL converts seconds back to int milliseconds. At the upper
bound, rounding down
+ // by less than one second keeps the timeout positive instead of
overflowing or disabling it.
+ return Math.min((millis - 1L) / 1000L + 1L, Integer.MAX_VALUE / 1000L);
}
/**
diff --git
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/utils/TestJdbcConnectionTimeouts.java
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/utils/TestJdbcConnectionTimeouts.java
index da3426ff26..ad80de7104 100644
---
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/utils/TestJdbcConnectionTimeouts.java
+++
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/utils/TestJdbcConnectionTimeouts.java
@@ -24,6 +24,8 @@ import java.util.Map;
import org.apache.gravitino.catalog.jdbc.config.JdbcConfig;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
public class TestJdbcConnectionTimeouts {
@@ -128,16 +130,29 @@ public class TestJdbcConnectionTimeouts {
assertPostgresConnectSeconds("1999", "2");
}
- @Test
- void testPostgresSecondsConversionAtMaxAllowedValue() {
- // The config caps the value at Integer.MAX_VALUE ms; the ceiling
conversion stays correct and
- // within int range at that bound (and is overflow-safe for the whole
positive long range).
- long millis = Integer.MAX_VALUE;
- long expectedSeconds = (millis - 1L) / 1000L + 1L;
- assertPostgresConnectSeconds(Long.toString(millis),
Long.toString(expectedSeconds));
- Assertions.assertTrue(expectedSeconds > 1L, "conversion must not collapse
to 1 second");
- Assertions.assertTrue(
- expectedSeconds <= Integer.MAX_VALUE, "converted seconds must fit the
driver int range");
+ @ParameterizedTest
+ @ValueSource(longs = {2147482999L, 2147483000L, 2147483001L, 2147483646L,
2147483647L})
+ void testPostgresTimeoutsFitDriverMilliseconds(long millis) {
+ Map<String, String> props =
+ Map.of(
+ DRIVER_KEY, "org.postgresql.Driver",
+ URL_KEY, "jdbc:postgresql://host:5432/db",
+ CONNECT_TIMEOUT_MS_KEY, Long.toString(millis));
+ Map<String, String> pooled =
+ JdbcConnectionTimeouts.connectionProperties(config(props),
props.get(URL_KEY));
+ Map<String, String> probe =
+ JdbcConnectionTimeouts.probeConnectionProperties(config(props),
props.get(URL_KEY));
+
+ // PostgreSQL multiplies its integer timeout seconds by 1000 before
setting socket timeouts.
+ // A value that fits an int in seconds can still overflow in that
conversion.
+ for (String seconds : probe.values()) {
+ int driverMillis = Math.multiplyExact(Integer.parseInt(seconds), 1000);
+ Assertions.assertTrue(driverMillis > 0, "driver timeout must remain
positive");
+ }
+ Assertions.assertEquals(Map.of(CONNECT_TIMEOUT, "2147483"), pooled);
+ Assertions.assertEquals(
+ Map.of(CONNECT_TIMEOUT, "2147483", SOCKET_TIMEOUT, "2147483",
"loginTimeout", "2147483"),
+ probe);
}
private static void assertPostgresConnectSeconds(String millis, String
expectedSeconds) {
diff --git a/docs/jdbc-catalog-connection-validation.md
b/docs/jdbc-catalog-connection-validation.md
index f4aa15c3f3..e8e7a7d9d1 100644
--- a/docs/jdbc-catalog-connection-validation.md
+++ b/docs/jdbc-catalog-connection-validation.md
@@ -92,7 +92,9 @@ unit:
- MySQL Connector/J — also used by the Doris, StarRocks, and OceanBase
catalogs: `connectTimeout`
and `socketTimeout` in milliseconds.
- PostgreSQL — also used by the Hologres catalog: `connectTimeout`,
`socketTimeout` and
- `loginTimeout` in seconds (rounded up, minimum 1).
+ `loginTimeout` in seconds (rounded up, minimum 1, maximum `2147483`). The
upper cap avoids
+ overflow when the driver converts seconds back to integer milliseconds; near
the millisecond
+ upper bound, it shortens the configured timeout by less than one second.
- ClickHouse: `connect_timeout` and `socket_timeout` in milliseconds (note the
underscore names).
It is applied differently to the two kinds of connection: