diff --git a/alembic/env.py b/alembic/env.py index 1586fb3..e9698c5 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -108,29 +108,30 @@ def run_migrations_offline() -> None: def do_run_migrations(connection: Connection) -> None: """Run migrations within a connection context. - Uses a PostgreSQL advisory lock to prevent concurrent migrations - from deadlocking when multiple containers start simultaneously. + Uses a PostgreSQL transaction-scoped advisory lock to prevent concurrent + migrations from deadlocking when multiple containers start simultaneously. + + IMPORTANT: The advisory lock MUST be acquired inside begin_transaction(), + not before context.configure(). Executing any SQL before configure() triggers + SQLAlchemy's autobegin, which causes Alembic to detect an "external transaction" + (_in_external_transaction=True) and skip its own transaction management. This + results in DDL being silently rolled back when the connection closes. """ - # Acquire advisory lock for PostgreSQL to serialize migrations - is_pg = connection.dialect.name == "postgresql" - if is_pg: - connection.execute(text("SELECT pg_advisory_lock(7483920165)")) - - try: - context.configure( - connection=connection, - target_metadata=target_metadata, - # Compare types for detecting column type changes - compare_type=True, - # Compare server defaults - compare_server_default=True, - ) - - with context.begin_transaction(): - context.run_migrations() - finally: - if is_pg: - connection.execute(text("SELECT pg_advisory_unlock(7483920165)")) + context.configure( + connection=connection, + target_metadata=target_metadata, + # Compare types for detecting column type changes + compare_type=True, + # Compare server defaults + compare_server_default=True, + ) + + with context.begin_transaction(): + # Acquire transaction-scoped advisory lock inside the transaction. + # pg_advisory_xact_lock auto-releases when the transaction commits/rolls back. + if connection.dialect.name == "postgresql": + connection.execute(text("SELECT pg_advisory_xact_lock(7483920165)")) + context.run_migrations() async def run_async_migrations() -> None: diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3b4daa6..a3c91ab 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,12 @@ For upgrade instructions, see [Upgrading](#upgrading) at the bottom. ## [Unreleased] +## [6.2.14] - 2026-02-13 + +### Fixed + +- **PostgreSQL migrations silently rolled back** — The advisory lock used to serialize concurrent migrations was acquired before Alembic's `context.configure()`, triggering SQLAlchemy's autobegin. Alembic detected this as an external transaction and skipped its own transaction management, so DDL changes (new columns, tables) were never committed. Switched to `pg_advisory_xact_lock()` inside the transaction block so Alembic properly commits. Fixes [#70](https://github.com/GeiserX/Telegram-Archive/issues/70). + ## [6.2.13] - 2026-02-11 ### Fixed diff --git a/pyproject.toml b/pyproject.toml index 7582ebf..0e681c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "telegram-archive" -version = "6.2.10" +version = "6.2.14" description = "Automated Telegram backup with Docker. Performs incremental backups of messages and media on a configurable schedule." readme = "README.md" requires-python = ">=3.14"