
При создании кейса на в моем тренажере, который я рассказывал ранее , возникло следующее: добавление пустой колонки выглядит безобидной миграцией: переписывать данные не нужно. Но такой ALTER TABLE может остановить новые чтения таблицы ещё до изменения её структуры. Достаточно старой транзакции, которую забыли завершить. Поясню эту цепочку на отдельном PostgreSQL 17.11 с таблицей orders из одной строки. Сначала первая сессия выполняет BEGIN и SELECT id FROM orders, после чего остаётся открытой. Сам запрос уже завершён, но транзакция продолжает держать AccessShareLock. Это не мешает другому обычному SELECT: такие блокировки совместимы. Проверка до запуска миграции возвращает строку без ожидания старой транзакции. Затем вторая сессия запускает ALTER TABLE orders ADD COLUMN note text. Для этого изменения нужна AccessExclusiveLock, несовместимая с блокировкой первой сессии. Миграция встаёт в очередь. Если теперь третья сессия выполнит SELECT id FROM orders, она тоже будет ждать. Её AccessShareLock совместима с уже выданной блокировкой первой сессии, однако впереди стоит запрос миграции на исключительный доступ. На снимке консоли видна вся цепочка. Сессия old_tx находится в состоянии idle in transaction и держит выданную блокировку: granted = t. У migration блокировка ещё не выдана, а blocked_by указывает на old_tx. Новый запрос api также имеет granted = f, но его blocked_by указывает уже на migration. Таким образом, источник ожидания и непосредственный блокировщик запроса — разные сессии. После ROLLBACK в old_tx миграция завершается, а новый SELECT получает результат. Читать далее
Continue to Habr →You are being forwarded in 6s — stay on this page