Visitar URL original
Connection.isValid(timeout) hangs indefinitely after network interruption during PGCopyOutputStream COPY · Issue #3957 · pgjdbc/pgjdbc · GitHub
Skip to content

Connection.isValid(timeout) hangs indefinitely after network interruption during PGCopyOutputStream COPY #3957

Closed

Description

@jptx1234

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 <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

  1. vlsi commented on Mar 14, 2026

    @vlsi
    Member

    Fixed in #3960

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions