Closed
@mastermatt

Description

I've been debugging this issue kibae/pg-logical-replication#20 and narrowed it down to the Client's end method not resolving (or calling the callback) under certain error conditions.

Minimal reproduction:

const client = new Client(connOpts)
await client.connect()

setImmediate(() => {
  client.connection.stream.resetAndDestroy() // simulate a network outage or the db going offline e.g. ECONNRESET

  setImmediate(async () => {
    await client.end() // <-- never resolves
  })
})

Notably, by the time .end is called, the connection and its stream (a new.Socket instance) have already emitted their close and end events. However, .end relies on an end event from the connection to resolve, unless the connection never connected to begin with.
I believe the check to have .end resolve early should not only look at the _connecting flag, but should also check if the socket has been destroyed.

// if we have never connected, then end is a noop, callback immediately
if (!this.connection._connecting) {

I believe these issues are related:

  1. Connection hang on the server side after ended #2329
  2. Bug in pool.end () never returns when trying to close cleanly #2341
  3. pg version 8.7.1 hangs on await db.end() but before version 8 doesn't #2648