{"id":34869226,"url":"https://github.com/modern-python/db-retry","last_synced_at":"2026-04-11T17:33:58.516Z","repository":{"id":299990613,"uuid":"1004795927","full_name":"modern-python/db-retry","owner":"modern-python","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-11T15:50:24.000Z","size":72,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-11T17:26:27.436Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/modern-python.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-06-19T07:35:45.000Z","updated_at":"2026-04-11T15:50:01.000Z","dependencies_parsed_at":"2025-06-19T10:26:41.625Z","dependency_job_id":null,"html_url":"https://github.com/modern-python/db-retry","commit_stats":null,"previous_names":["modern-python/pg-tools","community-of-python/db-try","modern-python/db-retry"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/modern-python/db-retry","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modern-python%2Fdb-retry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modern-python%2Fdb-retry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modern-python%2Fdb-retry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modern-python%2Fdb-retry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/modern-python","download_url":"https://codeload.github.com/modern-python/db-retry/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modern-python%2Fdb-retry/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31689762,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-11T13:07:20.380Z","status":"ssl_error","status_checked_at":"2026-04-11T13:06:47.903Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-25T23:03:24.096Z","updated_at":"2026-04-11T17:33:58.505Z","avatar_url":"https://github.com/modern-python.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# db-retry\n\nA Python library providing robust retry mechanisms, connection utilities, and transaction helpers for PostgreSQL and SQLAlchemy applications.\n\n## Features\n\n- **Retry Decorators**: Automatic retry logic for transient database errors\n- **Connection Factories**: Robust connection handling with multi-host support\n- **DSN Utilities**: Flexible Data Source Name parsing and manipulation\n- **Transaction Helpers**: Simplified transaction management with automatic cleanup\n\n## Installation\n\n### Using uv\n\n```bash\nuv add db-retry\n```\n\n### Using pip\n\n```bash\npip install db-retry\n```\n\n## ORM-Based Usage Examples\n\n### 1. Database Operations with Automatic Retry\n\nProtect your database operations from transient failures using ORM models:\n\n```python\nimport asyncio\nimport sqlalchemy as sa\nfrom sqlalchemy.ext.asyncio import create_async_engine, AsyncSession\nfrom sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column\nfrom db_retry import postgres_retry\n\n\nclass User(DeclarativeBase):\n    __tablename__ = \"users\"\n\n    id: Mapped[int] = mapped_column(primary_key=True)\n    name: Mapped[str] = mapped_column(sa.String())\n    email: Mapped[str] = mapped_column(sa.String(), index=True)\n\n\n# Apply retry logic to ORM operations (uses DB_RETRY_RETRIES_NUMBER, default 3)\n@postgres_retry\nasync def get_user_by_email(session: AsyncSession, email: str) -\u003e User:\n    return await session.scalar(\n        sa.select(User).where(User.email == email)\n    )\n\n\nasync def main():\n    engine = create_async_engine(\"postgresql+asyncpg://user:pass@localhost/mydb\")\n    async with AsyncSession(engine) as session:\n        # Automatically retries on connection failures or serialization errors\n        user = await get_user_by_email(session, \"john.doe@example.com\")\n        if user:\n            print(f\"Found user: {user.name}\")\n\n\nasyncio.run(main())\n```\n\nPer-callsite retry count override:\n\n```python\n@postgres_retry(retries=5)\nasync def create_order(session: AsyncSession, order: Order) -\u003e Order:\n    ...\n```\n\n### 2. High Availability Database Connections\n\nSet up resilient database connections with multiple fallback hosts:\n\n```python\nimport sqlalchemy as sa\nfrom sqlalchemy.ext.asyncio import create_async_engine, AsyncSession\nfrom sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column\nfrom db_retry import build_connection_factory, build_db_dsn\n\n# Configure multiple database hosts for high availability\nmulti_host_dsn = (\n    \"postgresql://user:password@/\"\n    \"myapp_db?\"\n    \"host=primary-db:5432\u0026\"\n    \"host=secondary-db:5432\u0026\"\n    \"host=backup-db:5432\"\n)\n\n# Build production-ready DSN\ndsn = build_db_dsn(\n    db_dsn=multi_host_dsn,\n    database_name=\"production_database\",\n    drivername=\"postgresql+asyncpg\"\n)\n\n# Create connection factory with timeout\nconnection_factory = build_connection_factory(\n    url=dsn,\n    timeout=5.0  # 5 second connection timeout\n)\n\n# Engine will automatically try different hosts on failure\nengine = create_async_engine(dsn, async_creator=connection_factory)\n```\n\n### 3. Simplified Transaction Management\n\nHandle database transactions with automatic cleanup using ORM:\n\n```python\nimport dataclasses\nimport datetime\nimport typing\n\nfrom schemas import AnalyticsEventCreate, AnalyticsEvent\nfrom db_retry import Transaction, postgres_retry\n\nfrom your_service_name.database.tables import EventsTable\nfrom your_service_name.producers.analytics_service_events_producer import AnalyticsEventsProducer\nfrom your_service_name.repositories.events_repository import EventsRepository\nfrom your_service_name.settings import settings\n\n\n@dataclasses.dataclass(kw_only=True, frozen=True, slots=True)\nclass CreateEventUseCase:\n    events_repository: EventsRepository\n    transaction: Transaction\n    analytics_events_producer: AnalyticsEventsProducer\n\n    @postgres_retry\n    async def __call__(\n            self,\n            event_create_data: AnalyticsEventCreate,\n    ) -\u003e AnalyticsEvent:\n        async with self.transaction:\n            model: typing.Final = EventsTable(\n                **event_create_data.model_dump(),\n                created_at=datetime.datetime.now(tz=settings.common.default_timezone),\n            )\n            saved_event: typing.Final[EventsTable] = await self.events_repository.create(model)\n            event: typing.Final = AnalyticsEvent.model_validate(saved_event)\n            await self.analytics_events_producer.send_message(event)\n            await self.transaction.commit()\n            return event\n\n```\n\n### 4. Serializable Transactions for Consistency\n\nUse serializable isolation level to prevent race conditions with ORM:\n\n```python\nfrom sqlalchemy.ext.asyncio import AsyncSession, create_async_engine\nfrom db_retry import Transaction\n\n\nasync def main():\n    engine = create_async_engine(\"postgresql+asyncpg://user:pass@localhost/mydb\")\n\n    async with AsyncSession(engine) as session:\n        strict_transaction = Transaction(\n            session=session,\n            isolation_level=\"SERIALIZABLE\",\n        )\n        # use strict_transaction where needed\n```\n\n## Configuration\n\nThe library can be configured using environment variables:\n\n| Variable                | Description                                      | Default |\n|-------------------------|--------------------------------------------------|---------|\n| `DB_RETRY_RETRIES_NUMBER` | Number of retry attempts for database operations | 3       |\n\nExample:\n```bash\nexport DB_RETRY_RETRIES_NUMBER=5\n```\n\n## API Reference\n\n### Retry Decorator\n- `@postgres_retry` - Decorator for async functions that should retry on database errors (uses `DB_RETRY_RETRIES_NUMBER`)\n- `@postgres_retry(retries=N)` - Override retry count per callsite\n\n### Connection Utilities\n- `build_connection_factory(url, timeout)` - Creates a connection factory for multi-host setups\n- `build_db_dsn(db_dsn, database_name, use_replica=False, drivername=\"postgresql\")` - Builds a DSN with specified parameters\n- `is_dsn_multihost(db_dsn)` - Checks if a DSN contains multiple hosts\n\n### Transaction Helper\n- `Transaction(session, isolation_level=None)` - Context manager for transaction handling; auto-rolls back on exit if no explicit `.commit()` or `.rollback()` was called\n\n## Requirements\n\n- Python 3.13+\n- SQLAlchemy with asyncio support\n- asyncpg PostgreSQL driver\n- tenacity for retry logic\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodern-python%2Fdb-retry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmodern-python%2Fdb-retry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodern-python%2Fdb-retry/lists"}