Что происходит с добавленной колонкой в транзакции при откате в PostgreSQL
Что проверяют
Знаете ли вы, что в PostgreSQL DDL транзакционный, и понимаете ли цену блокировки, которую ALTER TABLE держит до конца транзакции. Для бэкендера это вопрос не про теорию ACID, а про то, переживёт ли ваша миграция боевую нагрузку.
Как отвечать
- После
ROLLBACKколонки нет. В PostgreSQL изменения каталога подчиняются тем же правилам видимости, что и обычные строки, поэтомуBEGIN; ALTER TABLE t ADD COLUMN c int; ROLLBACK;оставляет таблицу ровно такой, какой она была. - Это не общее свойство баз: в Oracle и MySQL DDL фиксируется неявно, откатить его нельзя. Вопрос часто задают именно с расчётом на перенос чужого опыта.
- Пока транзакция открыта,
ALTER TABLEдержит на таблице блокировку самого сильного уровня. Другие транзакции не просто не видят новую колонку, они ждут на любом обращении к таблице, включая чтение. Длинная транзакция с DDL внутри это остановленное приложение. - Добавление колонки без значения по умолчанию это правка каталога, она мгновенная независимо от размера таблицы. В современных версиях и неизменяемое значение по умолчанию не приводит к переписыванию всех строк.
- Не откатывается то, что живёт вне транзакции: выданные значения последовательностей, отправленные письма, записанные файлы. А
CREATE INDEX CONCURRENTLYвнутри блока транзакции просто не запустится.
Где спотыкаются
Говорят «DDL откатить нельзя», перенося привычку из MySQL. В PostgreSQL можно, и на этом построены миграции, которые накатываются одной транзакцией целиком и либо применяются полностью, либо не оставляют следов.
Второе: помнят про откат, но забывают про блокировку и советуют «завернуть в транзакцию, так безопаснее». По данным безопаснее, по доступности опаснее: на нагруженной таблице ALTER сначала ждёт долгие читающие запросы, а сам в это время выстраивает за собой очередь из всех остальных. Спасает lock_timeout с повторными попытками.
Третье: ждут, что откат вернёт и потраченные значения последовательности. Счётчик не откатывается никогда, в нумерации остаются дырки, и это штатное поведение, а не повреждение данных.