Skip to content

Auto-reconnect on BrokenPipe outside transactions#510

Open
ntninja wants to merge 8 commits intodenodrivers:mainfrom
ntninja:main
Open

Auto-reconnect on BrokenPipe outside transactions#510
ntninja wants to merge 8 commits intodenodrivers:mainfrom
ntninja:main

Conversation

@ntninja
Copy link

@ntninja ntninja commented Feb 9, 2026

Reconnects to PostgreSQL when receiving BrokenPipe upon executing freestandig query or transaction start.

In-transaction queries are not retried, since we’d have to replay all previous queries and still risk inconsistency if a subsequently issued queries depended on data previously read during the aborted transaction. (We’d also have to keep a log of all previously returned data to verify that it hasn’t changed when reissuing the command!)
This patchset does convert BrokenPipe to TransactionError to let applications know that they should restart the transaction: If the database is really down, the subsequent .begin() will then fail with a ConnectionError.

This allow removes the calls to .commit() in the transaction error handling, see last commit message for why and why this doesn’t actually change application observable behaviour.

Fixes #436

@ntninja
Copy link
Author

ntninja commented Feb 9, 2026

This can be manually tested:

  1. Start up some Deno application using Pool, making sure it connects to the database
  2. Run ps aux | grep postgres to list all PostgreSQL instances
  3. Run kill <pids, …> to with the process IDs of all PostgreSQL instances serving the started application
  4. Perform some database-dependant task in the application and observe new PostgreSQL instance being started, rather than the application crashing with Deno.errors.BrokenPipe.

This applies to bewCloud, for instance.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds handling for Deno.errors.BrokenPipe to improve resiliency when a PostgreSQL connection is dropped, aiming to auto-reconnect for non-transaction queries and at transaction start, while surfacing TransactionError for in-transaction failures so applications can restart transactions (Fixes #436).

Changes:

  • Retry a non-transaction query once after BrokenPipe by closing and reconnecting.
  • Attempt to handle BrokenPipe during Transaction.begin() by reconnecting and retrying BEGIN.
  • Broaden TransactionError to carry a BrokenPipe cause; adjust error handling paths and connection shutdown.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
client.ts Adds BrokenPipe reconnect-and-retry logic around query execution.
query/transaction.ts Adds BrokenPipe handling in transaction lifecycle and wraps BrokenPipe as TransactionError.
client/error.ts Expands TransactionError cause type to include BrokenPipe.
connection/connection.ts Makes end() more robust by wrapping termination write in try/finally and waiting on writer readiness.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@bombillazo
Copy link
Collaborator

@ntninja Hey, thanks for the contribution, please make the relevant updates to ensure checks pass, and also review copilot suggestions.

@ntninja ntninja force-pushed the main branch 2 times, most recently from 742dd5e to 544c69b Compare February 17, 2026 23:59
@ntninja ntninja force-pushed the main branch 2 times, most recently from 67eda8c to 89fbd77 Compare February 18, 2026 01:24
…ng transaction command

Automatically reconnecting for an in-transaction queries is not supported, as
we’d have to replay all previous queries and still risk inconsistency if any
subsequently issued application queries depended on data previously read during
the disconnected transaction.

We’d *also* have to keep a log of all previously returned data to verify that
it hasn’t changed when reissuing the command – an unacceptable overhead,
particularly since applications must be able to handle transaction conflicts
anyway and this patch just makes disconnects look like any other
“please retry transaction” condition.

This commit also:
 * Removes useless `try…catch` blocks around function calls already containing
   an equivalent `try…catch` block and hence never catching anything.
 * Replaces calls to `commit()` in error handling with `rollback()` as
   `COMMIT`ing after an putting a transaction into an “aborted” state actually
   issues a `ROLLBACK` internally in PostgreSQL (probably some arcane
   compatiblity thing with software like this one), but using the wrong
   terminology is highly confusing. (*Actually* committing would risk applying
   inconsistent changes, resulting in data corruption!)
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.

BrokenPipe: Broken pipe (os error 32) not handled in reconnect logic

3 participants