Repository navigation
Description
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
- Run the demo code below
- During the COPY operation, interrupt the network (disconnect and reconnect network cable, or restart PostgreSQL server)
- 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 <pid> 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
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));
}
}
}
Activity
fix: release COPY lock on IOException to prevent connection hang (pgj…
fix: release COPY lock on IOException to prevent connection hang (pgj…
fix: release COPY lock on IOException to prevent connection hang (#3957)
vlsi commentedon Mar 14, 2026
Fixed in #3960
fix(copy): keep the COPY active when a read times out
fix(copy): keep the COPY active when a read times out
fix(copy): keep the COPY active when a read times out between messages
fix(copy): keep the COPY active when a read times out between messages
fix(copy): keep the COPY active when a read times out between messages
fix(copy): keep the COPY active when a read times out between messages
fix(copy): keep the COPY active when a read times out between messages
fix(copy): keep the COPY active when a read times out between messages
fix(copy): keep the COPY active when a read times out between messages
fix(copy): keep the COPY active when a read times out between messages