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

Базы данных

Миграции схемы: zero-downtime переименование колонки под нагрузкой

Суть Практический проект: переименуй колонку zero-downtime через expand-contract на нагруженной таблице Postgres, доказав lock-безопасность, батчевый backfill и совместимость с обеими версиями с доказательствами на каждом шаге.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 240 min

Читать про expand-contract — не то же самое, что переименовать колонку на нагруженной таблице, не уронив ни одного запроса. Подними таблицу Postgres с десятками миллионов строк, направь на неё трафик чтений/записей и мигрируй users.username в users.handle через все фазы — доказывая lock-безопасность и нулевую потерю данных измерениями, а не утверждениями.

Цель

Преврати ментальную модель юнита в воспроизводимый runbook: подготовь нагруженную таблицу под живым трафиком, разбей ломающий rename на безопасные фазы expand-contract, защити каждый DDL-шаг через lock_timeout, прогони батчевый backfill, который никогда не затапливает WAL, и подтверди, что обе версии кода остаются здоровыми на всём пути.

Проект
0 из 8
Цель

Переименуй колонку на таблице Postgres в 10M+ строк с username на handle с zero downtime, используя полную последовательность expand-contract, пока генератор нагрузки гоняет и старый (читает/пишет username), и новый (читает/пишет handle) клиент против базы всё время.

Требования
Критерии приёмки
  • Таймлайн ошибок запросов для v1 и v2 на всех шести фазах, показывающий ноль упавших запросов, относимых к несовместимости схемы — измеренный под живой нагрузкой, а не предположенный.
  • Пара lock-доказательств до/после: снапшот pg_locks во время конкурентного ALTER, показывающий путь abort-and-retry по lock_timeout, против того же ALTER, успешного в тихом окне.
  • Метрики бэкфилла: число батчей, строк на батч и график replication-lag (или WAL-rate), остающийся ограниченным для батчевого прогона, рядом с WAL-спайком одношагового UPDATE на одноразовой копии.
  • Абзац-разбор, называющий для каждой фазы инвариант, который держался (схема совместима с обеими версиями), и конкретный сбой, который он предотвратил (rename-простой, lock-заморозка, WAL-флуд).
Senior-стретч
  • Замени ручной SQL инструментом миграции (Atlas или golang-migrate): подключи сериализацию раннеров через pg_advisory_lock, lock_timeout + ретраи и non-transactional аннотацию на CREATE INDEX CONCURRENTLY по handle. Проверь indisvalid после билда.
  • Воспроизведи lock-queue инцидент намеренно: удержи 60-секундный аналитический SELECT, выстрели ALTER с lock_timeout = 0 и зафиксируй 503 и исчерпание пула — затем перезапусти с lock_timeout = '2s' и покажи, что заморозки нет.
  • Добавь наблюдаемость миграций: алерт на ретраи миграции > 3, любой post-deploy indisvalid индекс, replication lag > 10 с во время бэкфилла и длительность миграции > 30 с — затем намеренно сработай каждый алерт.
  • Подключи Squawk в CI на директории миграций и скорми ему небезопасные одношаговые формы (RENAME COLUMN, CREATE INDEX без CONCURRENTLY, ADD COLUMN с volatile DEFAULT); покажи, что каждая ловится на этапе PR.
Итог

Это цикл, который ты будешь запускать для каждого ломающего изменения схемы в продакшне: разбей на фазы expand-contract, чтобы схема была совместима с обеими версиями кода в каждый момент, защити каждый DDL-шаг через lock_timeout, чтобы конкурентный ALTER прерывался и ретраил вместо заморозки таблицы, делай backfill батчами, чтобы WAL оставался ограниченным и реплики успевали, добавляй constraints через NOT VALID + VALIDATE, чтобы DML никогда не блокировался, и откатывайся вперёд — никогда разрушительной down-миграцией. Сделав это раз на нагруженной игрушечной таблице под живым трафиком, ты доводишь production-версию до уровня мышечной памяти.

Продолжить восхождение ↑Зачем нужно шардирование: потолок одного Postgres
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.