Message-ID: From: "jptx1234 (@jptx1234)" To: "pgjdbc/pgjdbc" Date: Wed, 11 Mar 2026 16:09:20 +0000 Subject: [pgjdbc/pgjdbc] issue #3957: Connection.isValid(timeout) hangs indefinitely after network interruption during PGCopyOutputStream COPY List-Id: X-GitHub-Author-Id: 8757243 X-GitHub-Author-Login: jptx1234 X-GitHub-Issue: 3957 X-GitHub-Repo: pgjdbc/pgjdbc X-GitHub-State: closed X-GitHub-Type: issue X-GitHub-Url: https://github.com/pgjdbc/pgjdbc/issues/3957 Content-Type: text/plain; charset=utf-8 **Describe the issue** When using `PGCopyOutputStream` to perform a COPY operation and the network is interrupted (e.g., network cable disconnected or PostgreSQL server restarted), calling `Connection.isValid(timeout)` after catching the exception hangs indefinitely instead of returning `false` within the specified timeout. **Driver Version?** 42.7.10 **Java Version?** 21 **OS Version?** Windows 11 **PostgreSQL Version?** PostgreSQL 18 **To Reproduce** 1. Run the demo code below 2. During the COPY operation, interrupt the network (disconnect and reconnect network cable, or restart PostgreSQL server) 3. Observe that `conn.isValid(5)` hangs indefinitely instead of returning within 5 seconds **Expected behaviour** `Connection.isValid(5)` should return `false` within 5 seconds after network interruption. **Actual behaviour** `Connection.isValid(5)` hangs indefinitely and never returns. **Logs** Thread dump captured via `jstack -l ` shows the main thread is stuck waiting on a lock in `QueryExecutorImpl.waitOnLock()` ``` "main" #1 [28072] prio=5 os_prio=0 cpu=343.75ms elapsed=21.93s tid=0x0000025914a32250 nid=28072 waiting on condition [0x000000dc522fe000] java.lang.Thread.State: WAITING (parking) at jdk.internal.misc.Unsafe.park(java.base@21.0.6/Native Method) - parking to wait for <0x00000006239b2788> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(java.base@21.0.6/LockSupport.java:371) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(java.base@21.0.6/AbstractQueuedSynchronizer.java:519) at java.util.concurrent.ForkJoinPool.unmanagedBlock(java.base@21.0.6/ForkJoinPool.java:3780) at java.util.concurrent.ForkJoinPool.managedBlock(java.base@21.0.6/ForkJoinPool.java:3725) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@21.0.6/AbstractQueuedSynchronizer.java:1712) at org.postgresql.core.v3.QueryExecutorImpl.waitOnLock(QueryExecutorImpl.java:295) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:395) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:526) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:436) at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:358) at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:343) at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:319) at org.postgresql.jdbc.PgConnection.isValid(PgConnection.java:1584) at io.github.jptx1234.pgjdbc_copy_demo.PgjdbcCopyDemoStream.copyData(PgjdbcCopyDemoStream.java:55) at io.github.jptx1234.pgjdbc_copy_demo.PgjdbcCopyDemoStream.main(PgjdbcCopyDemoStream.java:29) Locked ownable synchronizers: - <0x00000006251520f0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) ``` **Minimal Reproducible Example** ```java import org.postgresql.copy.PGCopyOutputStream; import org.postgresql.jdbc.PgConnection; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.util.UUID; public class PgjdbcCopyDemoStream { private static final String DB_URL = "jdbc:postgresql://localhost:5432/postgres"; private static final String DB_USER = "postgres"; private static final String DB_PASSWORD = "password"; private static final String TABLE_NAME = "copy_test"; public static void main(String[] args) throws Exception { try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) { createTable(conn); copyData(conn); } } private static void createTable(Connection conn) throws Exception { try (Statement stmt = conn.createStatement()) { stmt.execute("DROP TABLE IF EXISTS " + TABLE_NAME); stmt.execute("CREATE TABLE " + TABLE_NAME + " (id SERIAL PRIMARY KEY, data TEXT)"); } } private static void copyData(Connection conn) throws Exception { String copySql = "COPY " + TABLE_NAME + " (data) FROM STDIN WITH (FORMAT CSV)"; try (PGCopyOutputStream copyOut = new PGCopyOutputStream(conn.unwrap(PgConnection.class), copySql)) { System.out.println("Start copying. Please interrupt network or restart PostgreSQL server."); // Interrupt network during this phase for (int i = 0; i < 500; i++) { copyOut.write((UUID.randomUUID() + "\n").getBytes(StandardCharsets.UTF_8)); copyOut.flush(); Thread.sleep(100); } } catch (Exception e) { System.err.println("COPY failed: " + e.getMessage()); System.out.println("Check isValid(5)"); // conn.isValid(5) should return false within 5 seconds, but hangs indefinitely System.out.println("isValid: " + conn.isValid(5)); } } }