Skip to content

Fix unbounded reconnect loop when all hosts fail during connect - #1188

Open
steobrien wants to merge 1 commit into
porsager:masterfrom
steobrien:fix-multi-host-connect-loop
Open

Fix unbounded reconnect loop when all hosts fail during connect#1188
steobrien wants to merge 1 commit into
porsager:masterfrom
steobrien:fix-multi-host-connect-loop

Conversation

@steobrien

@steobrien steobrien commented Aug 7, 2026

Copy link
Copy Markdown

Connection's local retries counter is read in two places — error()'s options.host[retries + 1] guard and tryNext()'s options.host[retries] — and reset in the ready handler, but nothing ever increments it (closed() bumps options.shared.retries instead, and for connecting sockets it returns early before reaching that line anyway).

So with multiple hosts configured, a connect-phase failure always finds a "next host": error() swallows it, closed() reconnects, forever. The pending query never settles:

const sql = postgres({ host: 'localhost', port: 1 })
await sql`select 1` // rejects with ECONNREFUSED in ~10ms

const sql = postgres({ host: ['localhost', 'localhost'], port: [1, 1] })
await sql`select 1` // never settles

This change counts each failed attempt in closed() and stops reconnecting once the host list is exhausted. A socket error on the last host already rejects the pending query in error() with the original error (e.g. ECONNREFUSED), and a clean close with no hosts left now rejects with CONNECTION_CLOSED. The counter resets when the cycle ends, so the next query sweeps the full host list again; a successful connect resets it as before.

The added test mirrors the existing single-host ECONNREFUSED test — it times out on master and passes with the fix.

@steobrien
steobrien marked this pull request as ready for review August 7, 2026 21:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant