From 76757f648db30ec6d414333fc3a6178ad033ab0d Mon Sep 17 00:00:00 2001 From: GeiserX <9169332+GeiserX@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:05:40 +0100 Subject: [PATCH] fix: prevent PostgreSQL migrations from silently rolling back The advisory lock acquired before context.configure() triggered SQLAlchemy's autobegin, causing Alembic to detect an "external transaction" and skip its own commit. Switched to pg_advisory_xact_lock() inside begin_transaction() so DDL changes are properly committed. Fixes #70 --- alembic/env.py | 45 +++++++++++++++++++++++---------------------- docs/CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 3 files changed, 30 insertions(+), 23 deletions(-) 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"