Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading