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:

Reply via email to