Автоматичне тестування міграцій БД: де ламається викочування
Збій на викочуванні рідко трапляється в самій міграції — він дозріває в розходженні схеми.
Бойова база й репозиторій описують різні стани: зміну колись застосували вручну, щоб розблокувати реліз, у ланцюжку міграцій не зафіксували — і наступне викочування падає на неіснуючому об’єкті або створює дубль.
Перевірка змін схеми влаштована як порівняння знімка бойової бази з результатом прогону міграцій з нуля: без неї розходження випливає лише в момент збою.
Друга точка відмови — блокування. Зміна типу колонки або перебудова індексу на великій таблиці тримає блокування хвилинами: запис стає, черга з’єднань зростає, сервіс деградує.
На порожньому тестовому стенді така зміна проходить миттєво, тому рішення про викочування ухвалюють навмання. Прогін на копії бойових даних показує, скільки тримається блокування і чи переживе його пікове навантаження.
Третя — відкат зміни, який ніхто не перевіряв. Зворотні скрипти пишуть за залишковим принципом і не ганяють у конвеєрі.
Під час відкату релізу код повертається до попередньої версії, а схема залишається новою: запити падають на невідповідності типів, і відновлення затягується довше, ніж тривало саме викочування.
Закривається це циклом: застосувати міграції на копії бойових даних, відкотити, застосувати знову й звірити підсумкову схему з еталонним знімком. Тоді незворотна зміна видна до прода, а не після.
Що дає перевірка змін схеми до викочування: час і клас відмов
Поки зміни схеми перевіряються руками на копії бойової бази, кожне викочування містить паузу: інженер чекає, поки накотиться набір міграцій, і лише потім дивиться, чи піднявся застосунок.
На потоці з кількох релізів на тиждень це години очікування й окремий клас відмов — «упало на схемі», який ловиться вже після перемикання трафіку.
Автоматичний прогін міграцій у конвеєрі закриває це на етапі збірки. Ізольоване середовище бази розгортається зі знімка на тій самій версії PostgreSQL, що й бойова: накат усіх міграцій по порядку, відкат до попереднього стану й повторний накат. Час випуску перестає залежати від того, хто саме сьогодні чергує.
Далі перевіряються властивості, які руками майже не тестують. Зворотна сумісність схеми — застосунок попередньої версії продовжує працювати з новою схемою, отже відкат релізу не потребує відкату міграції.
Ідемпотентність міграцій — повторний запуск дає той самий результат, і міграцію, що впала на середині, можна безпечно прогнати знову.
Окрема перевірка — простій через блокування: тест замірює, скільки тримається виняткове блокування при зміні таблиці, і позначає операції, що зупиняють запис на живій базі. Такі кроки розбиваються на безпечні: додати колонку, заповнити фоном, перемкнути читання, видалити старе.
Підсумок вимірюваний: відмови, пов’язані зі схемою, зникають зі статистики випусків, повторні прогони не потребують ручного втручання, а передбачуваність випуску перестає залежати від конкретного інженера.
Підходи до прогону змін схеми: порожня база, копія, репліка
Достовірність прогону визначається не самим фактом запуску міграції, а тим, на якій базі він іде.
Порожня база ловить синтаксис, порядок застосування й оборотність зміни, але мовчить про те, що трапиться на бойових даних: наявні рядки в новому обов’язковому стовпці, конфлікт унікального індексу, перебудова великої таблиці. Саме на таких розходженнях найчастіше й падає викочування.
Тому в CI/CD зазвичай тримають не один варіант, а кілька — під різні стадії та різний рівень ризику.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Прогін на порожній базі | На кожен коміт: перевірка синтаксису, порядку застосування й відкату | Не відтворює конфлікти з наявними даними та об’ємні перебудови таблиць |
| Спрощена база з репрезентативною вибіркою | Швидкі перевірки, де потрібні дані, але повна копія надто важка | Розходження з бойовою схемою накопичується: тести проходять там, де реальне викочування впаде |
| Копія бойової схеми зі знеособленими даними | Передрелізна перевірка перед викочуванням на критичний контур | Потрібні знімок, знеособлення й регулярна актуалізація; підготовка займає час |
| Тіньова репліка | Оцінка тривалості блокувань і поведінки на реальному обсязі та потоці | Потребує ізоляції від бойового трафіку й окремих ресурсів, дорожча в підтримці |
Комбінуємо за зростанням ціни й достовірності: порожня база на кожен коміт, копія — на гілці перед викочуванням, тіньова репліка — точково для міграцій, які зачіпають великі таблиці або змінюють типи стовпців.
Спрощена база корисна як швидкий компроміс, але її потрібно періодично звіряти з бойовою схемою — інакше тести починають підтверджувати те, чого в реальності немає.
Як вибудувати перевірку міграцій у конвеєрі: етапи робіт
Міграції — єдиний крок розгортання, який змінює дані й майже завжди незворотний. Непомічене блокування на великій таблиці зупиняє запис прямо під час релізу, а розсинхрон схеми випливає вже після. Нижче — порядок робіт, який робить цю операцію передбачуваною.
-
Інвентаризація міграцій. Збираємо накопичені файли, відокремлюємо ідемпотентні від одноразових і позначаємо небезпечні: зміну типу колонки, перебудову великої таблиці, видалення стовпців.
-
Еталонна схема й версіонування схеми. Знімаємо дамп актуального стану й зберігаємо його разом із кодом — кожна міграція зобов’язана приводити структуру до еталона. З’являється детермінована перевірка замість «на тестовій базі начебто працювало».
-
Чисті середовища в конвеєрі. Піднімаємо порожню базу потрібної гілки й проганяємо два сценарії: накат усіх міграцій з нуля та оновлення зі стану минулого релізу.
-
Автотести на схему й дані. Порівнюємо структуру з еталоном, перевіряємо оборотність відкатних міграцій і цілісність даних на підготовленому наборі. Червоний тест блокує збірку до виправлення.
-
Перевірка блокувань під навантаженням. Проганяємо міграцію на обсязі, близькому до робочого, і знімаємо план виконання запиту: скільки тримається блокування, чи не перебудовується індекс повністю, чи вкладається операція у вікно обслуговування.
-
Регламент. Міграція потрапляє в реліз лише із зеленими перевірками, а накат у робочий контур вручну — через окрему заявку з фіксацією причини.
-
Передача. Віддаємо опис конвеєра, порядок чергування й розбір типових збоїв, щоб команда супроводжувала схему далі сама.
Що замовник отримує на руки: конфігурації, тести, регламенти
Робота закінчується не звітом, а комплектом, який ваша команда запускає й підтримує без нас. Усе, що налаштовано на тестовому контурі, передається в тому самому вигляді, у якому працює на основному, — з описом, чому вибрано такі пороги й як їх змінювати.
- Налаштування конвеєра безперервної поставки: кроки збірки, запуск міграцій на порожній базі та на копії бойових даних, умова зупинки випуску при розходженні схеми.
- Набір автотестів схеми: застосування й відкат кожної міграції, звірка таблиць, індексів, обмежень, значень за замовчуванням і прав після накату.
- Налаштування середовищ і скрипти підйому/скидання: розгортання бази за лічені хвилини, передбачувані тестові дані, скидання без залишкових об’єктів між прогонами.
- Політики доступу до копій: кому й на який строк видаються знеособлені копії, де вони зберігаються, як відкликаються права й що потрапляє в журнал.
- Регламент випуску: дії при конфлікті міграцій, правила сумісності сусідніх версій схеми, точка відкату й відповідальний за рішення.
- Комплект документації: карта конвеєра, опис середовищ, перелік перевірок із прикладами падінь, порядок розбору інцидентів.
Окремо фіксуємо, як додати нову перевірку або середовище своїми силами: зазвичай це копія наявного блоку зі зміненими параметрами. Такий комплект знімає залежність від підрядника — розвивати тестування міграцій можна всередині команди.
Кейс: як схема перестала ламати викочування після впровадження перевірок
До: схема розходилася між середовищами
Міграції випускалися тим самим конвеєром, що й код, але перевірялися очима: рев’юер читав текст міграції й звіряв його з поточною схемою. Тестовий стенд наздоганяв прод вручну і не завжди.
З десяти релізів два-три падали на застосуванні — блокування таблиці на великому обсязі даних, конфлікт із уже наявним індексом, знята колонка, яку ще читав попередній реліз коду.
Дрейф схеми накопичувався місяцями: на тестовому стенді не вистачало частини полів, а на проді жили індекси, яких не було в жодному репозиторії.
Що змінили
Кожну міграцію проганяємо двічі: на порожній базі — перевірка застосовності з нуля, і на свіжій копії прод-схеми — перевірка сумісності з накопиченими даними. Зворотний сценарій обов’язковий: без робочого відкату міграція не проходить стадію.
Порядок змін тримаємо в журналі Liquibase, тому відкат кроку назад — така сама автоматична операція, як і застосування. Знімки схеми з dev, тестового стенда й прода порівнюються на кожній збірці: розходження блокує викочування із зазначенням конкретного об’єкта.
Що стало
Час випуску скоротився з двох-трьох днів до кількох годин: стадія перевірки йде паралельно збірці й не потребує ручного прогону перед релізом. Відмов на викочуванні — один за квартал замість двох-трьох із десяти релізів.
Розходжень між середовищами було більше тридцяти об’єктів, стало нуль — перевірка не пропускає їх далі збірки. Розбір інцидентів на етапі зміни схеми пішов із практики: конфлікт видно на стадії перевірки, а не на бойовій базі під навантаженням.
Чим прогін на порожній базі відрізняється від прогону на копії бойової?
Порожня база перевіряє лише те, що міграція синтаксично коректна й застосовується в потрібному порядку: таблиці створюються, індекси стають, зовнішні ключі валідні. Достовірність такої перевірки закінчується там, де починається поведінка на даних.
Планувальник запитів на порожніх таблицях вибирає один план, а на бойових обсягах — інший: обхід індексу, непомітний на десятку рядків, на реальному розподілі дає блокування й зростання часу відгуку.
Копія бойової закриває цей розрив: зріз зі збереженим обсягом тестових даних і розподілом значень — рідкісні гілки, порожні значення, дублікати, рядки граничної довжини — змушує обмеження працювати по-справжньому.
Обмеження цілісності спрацьовує тільки тоді, коли в таблиці є рядок, який воно має відхилити; на порожній базі цей сценарій не відтворюється.
Окрема причина розходжень — діалект бази. На іншому діалекті не відтворюється приведення типів, порівняння рядків з урахуванням регістру, порядок сортування й обробка порожніх значень. Міграція проходить у конвеєрі й падає вже на бойовій, коли відкат коштує найдорожче.
Тому тримаємо обидва прогони: порожня база ловить помилки в самій міграції та в порядку застосування з нуля, копія бойової відповідає за достовірність перевірки на реальних обсягах.
Зріз знеособлюємо, щоб персональні дані не потрапляли в тестовий контур, але склад стовпців, типи й розподіл зберігаємо — інакше перевірка знову стає формальною.
Сумніви інженера: складні доопрацювання схеми, дрейф, відкати
Заперечення проти автотестів міграцій зазвичай предметні: схема вже живе в проді, частина даних під обмеженнями, а зміна може тримати таблицю годинами. Нижче — як кожен із цих випадків перевіряється на стенді, а не в бою.
Схема в проді вже розійшлася з міграціями — тести це покажуть?
Стенд піднімається з накоченого ланцюжка міграцій, і розходження структури з продом видно на першому ж прогоні — до викочування, а не після. Далі розходження або закривають новою міграцією, або приводять прод до ланцюжка окремим скриптом, який теж проходить перевірку.
Зміна схеми йде довго й блокує таблицю — що з цим роблять?
Прогін на копії продового обсягу показує, скільки реально тримається блокування. Якщо довше за допустиме вікно — зміну розбивають на кроки: додати колонку без обмеження, заповнити порціями, перемкнути читання, зняти старе. Кожен крок — окрема міграція зі своїм тестом.
Як перевіряється зворотна сумісність і чи можливий відкат?
Тести піднімають поруч попередню й нову версію застосунку та перевіряють, що обидві працюють на одній схемі — тоді відкат викочування не потребує відкату бази.
Зворотний скрипт проганяється на даних, що залишилися після прямого; якщо відкат без втрат неможливий, це фіксується на погодженні заздалегідь.
Дані чутливі — як тестувати, якщо їх не можна копіювати?
Стенд отримує синтетичний набір з тією самою структурою й схожим розподілом значень, тому обмеження зовнішніх ключів і унікальності відтворюються повністю. Чутливі поля замінюють генераторами — перевірка йде на реальних правилах, а не на порожніх таблицях.
Обговоримо вашу задачу: що уточнимо й із чим повернемося
Щоб повернутися з оцінкою та планом, а не із загальними міркуваннями, потрібен розбір вашого конвеєра: як правки схеми проходять шлях від зміни в гілці до релізу й що відбувається між цими точками. Нижче — вступні, яких нам зазвичай достатньо, щоб порахувати обсяг робіт.
- СУБД та інструмент міграцій: чим накочуються зміни, чи версіонуються вони пофайлово, чи є транзакційна обгортка й окремий крок відкату.
- Обсяг і характер правок: скільки змін схеми на тиждень, чи є важкі перебудови великих таблиць і блокувальні операції.
- Що вже перевіряється: ручні прогони на копії, рев’ю запитів, самописні скрипти — і на якому кроці вони дають збій.
- Середовища й дані: чи є окремий контур для прогонів, як готується тестовий набір, чи знеособлюються дані.
- Обмеження: допустиме вікно недоступності, вимоги до відкату, порядок погодження релізів.
- Доступи й відповідальність: хто власник репозиторію, раннерів і налаштувань середовищ, хто прийме регламенти на боці експлуатації.
Після розбору повернемося зі схемою конвеєра, оцінкою та планом робіт, а за підсумками впровадження передамо регламенти: що запускається на кожну зміну, які перевірки блокують злиття, як діяти при падінні.
Далі залишаємося на супроводі — доналаштовуємо правила під нові типи міграцій і розбираємо інциденти. Опишіть задачу — повернемося з оцінкою та планом.







