У PostgreSQL кожна команда бере на таблицю блокування певного режиму. Режими конфліктують між собою за таблицею сумісності:
SELECT-ACCESS SHARE(найслабший);INSERT/UPDATE/DELETE-ROW EXCLUSIVE;CREATE INDEX-SHARE(блокує запис);- більшість
ALTER TABLE,DROP,TRUNCATE,VACUUM FULL-ACCESS EXCLUSIVE(конфліктує з усім, навіть ізSELECT).
Ланцюжок, що кладе прод:
- Довгий запит (звіт, забута транзакція в
idle in transaction) тримаєACCESS SHAREна таблиці. - Міграція виконує
ALTER TABLE orders ADD COLUMN note text- їй потрібенACCESS EXCLUSIVE, і вона стає в чергу за звітом. - Усі нові запити до
orders, навіть простіSELECT, стають у чергу заALTER TABLE, бо черга блокувань упорядкована. - Таблиця фактично недоступна, доки не завершиться звіт - хвилини чи години. Пул з'єднань застосунку вичерпується, сайт лягає.
Сама зміна після отримання блокування займає мілісекунди - проблема саме в очікуванні.
Захист:
SET lock_timeout = '3s';
ALTER TABLE orders ADD COLUMN note text;
Якщо блокування не отримано за 3 секунди, команда падає й звільняє чергу. Міграцію повторюють (автоматично з паузами чи вручну).
Ще правила:
- Перед міграцією перевіряти
pg_stat_activityна довгі запити йidle in transaction. - Налаштувати
idle_in_transaction_session_timeout, щоб забуті транзакції не жили вічно. - Робити кожну міграцію короткою: одна зміна схеми - одна транзакція, без масового оновлення даних в тій самій транзакції.