Repository navigation
fix: retry a connection SQLAlchemy invalidated - #69
Merged
Merged
Conversation
Retry any DBAPIError marked connection_invalidated, which covers asyncpg's InterfaceError on a pooled connection that closed while idle. Document that the retried coroutine must open its own Transaction.
This was referenced Oct 16, 2026
Merged
Merged
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.
Closes #68.
postgres_retrynow also retries aDBAPIErrorthat SQLAlchemy markedconnection_invalidated. That covers asyncpg'sInterfaceError: connection is closed, which a pooled connection that died while idle raises on its next use. Before, that error failed the request, sopool_pre_ping=Truewas the only protection after a Postgres restart or failover.The check uses SQLAlchemy's flag instead of adding
asyncpg.InterfaceErrorto the tuple. MostInterfaceErrorraise sites are deterministic misuse ("another operation is in progress", wrong argument counts, cursor errors), and retrying those only delays the failure. The asyncpg dialect sets the flag fromis_closed()on the driver connection. Raw asyncpg errors stay class-only: without the flag, the closed case can only be recognised by its message.The shared-session half of #68 is a documentation change. A retry cannot recover a session that is still inside the failed transaction, and
postgres_retrycannot see the session to roll it back. The README now says the retried coroutine must open its ownTransaction, and the first example does that.Changes:
db_retry/retriable.py:_is_retriable_linkacceptsconnection_invalidatedbefore the asyncpgisinstance.docs/adr/0002: amendment recording the flag as the second axis that ADR left room for.CONTEXT.md: "Retriable error" now includes the flag.README.md: the retry precondition, the first example rewritten to useTransaction, and the API reference.Tests:
test_retriable.py: aconnection_invalidatederror is retriable, and anInterfaceErroron a live connection is not.test_retry.py: terminates the backend of the one pooled connection, waits for asyncpg to see it close, then checks that a retriedTransactionsucceeds on the second attempt. It fails without the change inretriable.py.Downstream: chat-app has six use cases that retry on an injected session without
Transaction(authenticate_user,check_token_revoked,fetch_chat,fetch_chats,fetch_messages,fetch_user). They need a follow-up once this is released.