diff --git a/README.md b/README.md index 59661e7..fe9337b 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ pip install db-retry ### 1. Database operations with automatic retry -Retry a coroutine on serialization failures and lost connections. The retried coroutine must open its own transaction with `Transaction`, so that a failed attempt is rolled back before the next one starts. If it reuses a session that is still inside the failed transaction, the next attempt raises `PendingRollbackError` and the retry gives up. +Retry a coroutine on serialization failures and lost connections. Either error ends the whole Postgres transaction, so the retried coroutine has to own its transaction from start to finish: open it with `Transaction`, as below, or work on a fresh session it creates itself. A coroutine that runs on a session someone else began cannot be retried. Postgres has already discarded the statements that ran before it, and SQLAlchemy refuses the next attempt with `PendingRollbackError` instead of committing part of the work. ```python import asyncio @@ -197,7 +197,7 @@ export DB_RETRY_RETRIES_NUMBER=5 - `@postgres_retry` - Decorator for async functions that should retry on database errors (total attempts, first call plus retries, from `DB_RETRY_RETRIES_NUMBER`) - `@postgres_retry(retries=N)` - Override the total attempts (first call plus retries) per callsite - Retries a serialization failure (`40001`), a connection error (class `08`), and any `DBAPIError` that SQLAlchemy marks with `connection_invalidated`. The last one covers asyncpg's `InterfaceError: connection is closed` on a pooled connection that died while idle, which is what `pool_pre_ping=True` otherwise catches -- The retried coroutine must open its own `Transaction`; a retry on a session left in a failed transaction raises `PendingRollbackError` +- The retried coroutine must own its whole transaction, through `Transaction` or a fresh session; on a session another caller began, the next attempt raises `PendingRollbackError` - Retries also fire when the retriable `asyncpg` error is wrapped by [`advanced-alchemy`](https://github.com/litestar-org/advanced-alchemy)'s `wrap_sqlalchemy_exception()` (i.e. surfaced as `RepositoryError` / `IntegrityError`); the handler walks the `__cause__` / `__context__` chain. ### Connection utilities