Repository navigation
fix(database): PostgreSQL recovers after a failed reconnect (#242) - #243
Merged
Merged
Conversation
#236 reconnects a link the server drops DURING a statement, but execute()/fetch()/fetch_one() build the cursor first. On a link that is already closed - the state _reconnect leaves behind when it closed the old link and the replacement connect() failed (a failover before the new primary answers) - self._conn.cursor() raised "connection already closed" before the reconnect path could run, and every later statement wedged there until the process restarted. Fix: _ensure_connection() rebuilds a dead, non-transactional link BEFORE a cursor is opened on it, called at the top of execute/fetch/fetch_one. A reconnect that fails again propagates but leaves the link dead, so the next statement retries instead of giving up for good. Verified against a REAL PostgreSQL (no mocks): a real dropped backend, a real failed reconnect (connect args aimed at a dead port), then real recovery when the good target returns. Mutation-proven - the new test fails on the old code with exactly "InterfaceError: connection already closed", passes with the fix; the five #236 cases stay green. Parity note: PostgreSQL reconnect-after-drop is a Python-only feature today, so this bug exists only in Python. PHP/Ruby/Node never re-establish a dropped PostgreSQL link at all - that is a separate parity gap (porting #236), not this fix. Signed-off-by: Andre van Zuydam <andrevanzuydam@gmail.com> Co-Authored-By: Tina4 <82961293+tina4stack@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Andre van Zuydam <andrevanzuydam@gmail.com>
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #242 — PostgreSQL never recovered after a failed reconnect
Root cause (reproduced against real PG, not the reporter's code-read)
#236 recovers a link that dies during a statement, but
execute/fetch/fetch_onebuild the cursor first. On an already-closed link — the state
_reconnectleaves when itclosed the old link and the replacement
connect()failed (a failover before the newprimary answers) —
self._conn.cursor()raisesInterfaceError: connection already closedbefore the reconnect path can run, so every later statement wedged there until restart.
Fix
_ensure_connection()rebuilds a dead, non-transactional link before a cursor is opened,called at the top of
execute/fetch/fetch_one. A reconnect that fails again propagatesbut leaves the link dead, so the next statement retries rather than giving up.
Verification — NO MOCKS
Real PostgreSQL: a real dropped backend, a real failed reconnect (connect args aimed at a
dead port = the new primary not up yet), then real recovery once the good target returns.
Mutation-proven: the new test fails on the old code with exactly
InterfaceError: connection already closed, passes with the fix; the five existing #236 cases stay green.Parity
Reconnect-after-drop is a Python-only feature right now, so this bug exists only in
Python. PHP/Ruby/Node never re-establish a dropped PostgreSQL link at all — a separate,
larger parity gap (porting #236), flagged for its own task, not folded in here.
🤖 Generated with Claude Code