awesome-everything EN
↑ Обратно к восхождению

Базы данных

Advisory-блокировки, инструменты миграций и координация деплоя

Суть pg_advisory_lock сериализует конкурентные запуски миграций; выбор инструмента (Atlas, pgroll, Prisma, Drizzle) определяет, сколько безопасности автоматизировано, а сколько делается вручную.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Kubernetes-деплоймент масштабируется с нуля до трёх реплик одновременно. Все три пода запускаются и каждый пытается выполнить ожидающую миграцию. Без координации все три применяют её параллельно — один успешно, два падают с ошибками дублирования constraint, приложение запускается наполовину в неизвестном состоянии.

Транзакционная семантика DDL в Postgres

Большинство DDL в Postgres транзакционный: BEGIN; CREATE TABLE; ALTER TABLE; COMMIT; либо всё выполняется, либо всё откатывается атомарно. Конкурентные сессии видят изменение только после COMMIT (изоляция снимком MVCC).

Исключения, которые не могут выполняться внутри блока транзакции:

  • CREATE INDEX CONCURRENTLY
  • REINDEX CONCURRENTLY
  • ALTER TYPE ... ADD VALUE (в PG 11 и ниже; транзакционный начиная с PG 12+)

Инструменты миграций по умолчанию оборачивают каждую миграцию в транзакцию. Любая миграция с CONCURRENT-операцией требует аннотации нетранзакционного режима. Большинство инструментов поддерживают это:

  • Atlas: -- atlas:txmode none
  • golang-migrate: -- migrate:notransaction
  • Prisma: не поддерживается нативно; используйте prisma db execute с raw SQL-файлом

Если вы смешиваете CONCURRENT-операцию с другим DDL в одном файле миграции — разбейте их на два файла.

Паттерн advisory-блокировки для сериализации запуска миграций

Несколько серверов приложения, запускающихся одновременно, могут каждый попытаться выполнить ожидающие миграции. Без координации два процесса могут применить одну и ту же миграцию дважды или конфликтовать на таблице schema_migrations.

Стандартное решение — advisory-блокировка Postgres в начале миграции:

-- Берётся перед выполнением любых миграций:
SELECT pg_advisory_lock(12345);  -- 12345 = стабильный целочисленный ключ для этого приложения

-- Здесь выполняются миграции ...

-- Освобождается после завершения всех миграций:
SELECT pg_advisory_unlock(12345);

pg_advisory_lock блокируется до получения блокировки. Первый сервер получает её и выполняет миграции; последующие ждут. Когда первый завершает и освобождает блокировку, следующий сервер получает её — но обнаруживает все миграции уже применёнными и ничего не делает.

Большинство инструментов миграций реализуют это автоматически:

  • golang-migrate: берёт pg_advisory_lock(hashInt64(databaseName)) при запуске.
  • Prisma: использует таблицу _prisma_migrations с блокировкой на уровне строк.
  • Atlas: использует pg_advisory_lock с настраиваемым ключом.

Для неблокирующего поведения при запуске используйте pg_try_advisory_lock с циклом повторов:

-- Возвращает true при успешном получении, false если уже занята
SELECT pg_try_advisory_lock(12345);
Инструментlock_timeoutПовторыDDL-lintНетранз. режим
AtlasДа (конфиг)Да (экспоненциальный откат)Да (встроен + Squawk)Да (txmode none)
pgrollДаДаЧерез безопасность view-слояН/Д (обрабатывает CONCURRENT внутренне)
Prisma MigrateВручную (SET в SQL)ВручнуюSquawk (отдельный CI-шаг)Ограничен
golang-migrateВручную (SET в SQL)ВнешнийSquawk (отдельный CI-шаг)Да (notransaction)
FlywayВручную (SET в SQL)ВручнуюТолько Pro-версияДа

Координация деплоя: миграция до раскатки подов

Три распространённых паттерна:

  1. Pre-deploy job (рекомендован): k8s Job или CI-шаг запускает migrate up против продакшна до rolling deploy. Деплой кода заблокирован успехом миграции. Старые поды обрабатывают трафик во время миграции; схема аддитивна (только expand). Это чистейший паттерн — чёткое разделение ответственности.

  2. Init container на каждом поде: каждый новый под запускает migrate up при запуске; advisory-блокировка их сериализует. Просто, но медленно — каждый под ждёт проверки миграции, даже если ничего не ожидает.

  3. Отдельный pipeline миграций: миграция запускается вручную или в собственном ритме, полностью отвязана от деплоя кода. Требует человеческой координации; ненадёжна в масштабе.

Старший дефолт 2026: отдельный pre-deploy job (паттерн 1), заблокированный на успехе миграции. Задание миграции выдаёт метрики; любой сбой страницует on-call и блокирует раскатку кода.

Ландшафт инструментов миграций (2026)
Atlas (рекомендован для Postgres-команд)
Декларативный + lint + повторы + dry-run
pgroll (нулевой простой через виртуальные схемы)
Views + триггеры, PG 14+
Prisma Migrate (стеки TS/Node)
Schema-first, генерирует SQL миграций
Drizzle (TS/Node, лёгкий)
Schema-first, похож на Prisma
golang-migrate (независим от языка)
Простые up/down SQL-файлы
Squawk (линтер)
CI: ловит небезопасный DDL в PR
Advisory lock при запуске
Стандарт в golang-migrate, Atlas
Почему это работает

Почему pgroll использует views вместо прямой схемы? Цель pgroll — сделать декларативную миграцию, переименовывающую колонку, похожей для разработчика на одношаговую операцию. Под капотом нужно одновременно поддерживать совместимость для старого и нового кода — views с триггерами — единственный нативный механизм Postgres для представления двух разных имён колонок, поддерживаемых одним хранилищем, без изменений приложения для управления dual-write вручную. Цена — операционная сложность: отладка инцидента миграции требует понимания view-слоя.

Викторина

Три k8s-пода запускаются одновременно и каждый пытается выполнить ожидающие миграции. Без advisory-блокировки каков наиболее вероятный режим сбоя?

Викторина

Какой инструмент миграций предлагает встроенный DDL-lint, конфигурацию lock_timeout, повторы и нетранзакционный режим для Postgres в 2026?

Вспомните перед уходом
  1. 01
    Как pg_advisory_lock предотвращает дублирующееся выполнение миграций конкурентными подами и как инструменты это реализуют?
  2. 02
    Каков рекомендованный паттерн координации деплоя для k8s-приложения и почему он предпочтительнее выполнения миграций при запуске пода?
  3. 03
    Почему CREATE INDEX CONCURRENTLY должен выполняться вне транзакции и как это настроить в инструментах миграций?
Итог

DDL в Postgres транзакционный — BEGIN; ALTER TABLE; COMMIT; откатывается атомарно при сбое — за исключением CREATE INDEX CONCURRENTLY и нескольких других операций, которые должны выполняться вне блока транзакции и требуют аннотации на уровне инструмента. Когда несколько подов запускаются одновременно, все могут попытаться выполнить ожидающие миграции; pg_advisory_lock сериализует их, позволяя в момент времени работать только одной сессии. Atlas (декларативная схема, встроенный lint, lock_timeout, повторы, dry-run) — наиболее полный инструмент миграций Postgres в 2026; pgroll добавляет виртуально-схемный нулевой простой для команд с частыми ломающими изменениями; Prisma/Drizzle предоставляют schema-first эргономику для TS-стеков. Рекомендованный паттерн деплоя — pre-deploy job миграции, заблокированный до rolling deploy подов, с CI-lint Squawk, блокирующим небезопасные PR до слияния.

Связанные уроки
встречается в140
Продолжить восхождение ↑Таксономия сбоев миграций и дисциплина продакшна
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.