Якість і безпека
Міграції бази даних: змінюйте схему й дані контрольовано
Зміна структури таблиці часто зачіпає дані, код застосунку, фонові завдання й кілька запущених версій програми. Міграція бази даних — це керований…
Зміна структури таблиці часто зачіпає дані, код застосунку, фонові завдання й кілька запущених версій програми. Міграція бази даних — це керований перехід між такими станами. Її правильність означає не тільки, що команда мови визначення даних (Data Definition Language, DDL) завершилася: старий і новий код мають коректно працювати з проміжною схемою, наявні рядки мають отримати визначений зміст, а спосіб відновлення має відповідати тому, що вже змінено.
У Designing Data-Intensive Applications (DDIA), другому виданні, Мартін Клеппманн і Кріс Ріккоміні розглядають еволюцію форматів і схем разом із змінами коду: у великій системі різні версії коду можуть одночасно читати й записувати дані. Software Architecture: The Hard Parts розглядає поділ операційних даних як компроміс між незалежною зміною та зв’язками, транзакціями й спільною інфраструктурою. Для міграції це означає, що сумісність і навантаження треба планувати разом, а шаблон переходу обирати за конкретними гарантіями сховища та споживачів. ([DDIA, 2nd ed., розділ 5](https://www.oreilly.com/library/view/designing-data-intensive-applications/9781098119058/ch05.html); [Software Architecture: The Hard Parts, розділ 6](https://www.oreilly.com/library/view/software-architecture-the/9781492086888/))
Після розділу читач може:
- розрізнити структурну міграцію, міграцію даних і запис історії застосування;
- перевірити сумісність схеми зі старою та новою версіями коду;
- оцінити переписування таблиці, блокування, сканування, індекси й пакетне заповнення;
- спланувати розширення, перенесення та скорочення схеми з перевірками між етапами;
- визначити, чи можна відкотити структуру, дані або поведінку після перемикання джерела істини;
- назвати показники, за якими міграцію треба продовжити, сповільнити або зупинити.
Приклади нижче навчальні й синтетичні. SQL (Structured Query Language — структурована мова запитів) наведено для пояснення, його не запускали. Поведінку команд PostgreSQL прив’язано до документації PostgreSQL 18; інші версії та системи керування базами даних можуть відрізнятися.
52.1. Три різні речі, які часто називають міграцією
Структурна міграція змінює об’єкти бази: таблиці, стовпці, типи, індекси, обмеження, подання чи тригери. У термінах SQL це переважно DDL, наприклад ALTER TABLE.
Міграція даних змінює або доповнює значення вже збережених рядків. Це зазвичай мова маніпулювання даними (Data Manipulation Language, DML), наприклад пакетний UPDATE. Заповнення нового стовпця, нормалізація значень і перетворення суми — зміни даних, навіть якщо вони виконуються під час розгортання нової схеми.
Історія версій міграцій фіксує, які міграційні кроки інструмент вважає застосованими. Наприклад, Rails зберігає номер виконаної міграції в таблиці schema_migrations. Цей запис допомагає інструменту визначити наступний крок, але сам по собі не доводить, що структура в каталозі бази не змінилася вручну або що кожен старий рядок містить правильне значення. Це розрізнення є висновком із того, що журнал версій і фактичні об’єкти та рядки — різні спостереження. (Rails 7.2, Active Record Migrations)
Отже, перевіряйте окремо: чи очікувана версія зареєстрована; чи фактична схема має потрібні об’єкти; чи дані задовольняють нові інваріанти. Відмінність між задуманим станом і фактичним називатимемо дрейфом схеми або даних. Він може виникнути через ручну зміну, неповне відновлення, помилку посеред міграції чи відсутню міграцію в одному середовищі.
Зміна одного стовпця також змінює контракт для його споживачів: вебзастосунку, черги, звіту, іншого сервісу або старого воркера, що ще виконується. Перелік споживачів та їхні версії належить до передумов плану, а не до прибирання після розгортання.
52.2. Сумісність коду зі схемою в часі
У простому локальному середовищі можна зупинити програму, змінити схему, заповнити дані й запустити нову версію. У розподіленому розгортанні старі вебпроцеси, черги, планувальники та нові екземпляри можуть працювати паралельно. Кожен із них може звернутися до тієї самої таблиці до, під час і після міграції.
Для кожного етапу запитайте:
- Чи може старий код читати нову схему?
- Чи може новий код читати старі рядки, які ще не заповнено?
- Куди пише кожна версія і яке значення є джерелом істини?
- Що станеться, якщо воркер або міграційний процес повторить операцію після збою?
- Яку умову треба перевірити перед видаленням старого поля чи старого формату?
Поширений спосіб організувати сумісність має три частини:
- Розширення (expand). Додайте нову структуру так, щоб старий код ще працював: наприклад, стовпець, який допускає
NULL, таблицю-приймач або новий індекс. - Перенесення (migrate). Розгорніть код, який уміє читати обидва стани, спершу забезпечте нові записи, а потім перенесіть старі дані малими повторюваними порціями. Перевіряйте результат.
- Скорочення (contract). Коли старі версії, черги, звіти й інтеграції більше не читають і не пишуть старий формат, перемкніть джерело істини, а згодом видаліть непотрібне поле чи сумісний шлях.
Цей порядок іноді називають Expand–Migrate–Contract (розширити — перенести — скоротити). Це схема планування, а не команда бази чи гарантія безперервної роботи. Між етапами потрібні перевірки; деякі зміни вимагають узгодженого перемикання або короткого вікна обслуговування.
Поки обидва поля використовуються, визначте джерело істини явно. Читання з резервним переходом до старого поля може допомогти читати неперенесені рядки. Подвійний запис може підтримати сумісність версій, але два окремі запити без спільної транзакції здатні завершитися частково. Якщо записи не можуть бути атомарними, потрібні інший механізм доставки, повтор і звірка — наприклад, журнал подій із гарантією повторної обробки. Після визначеної контрольної точки читання й записи переходять на нове джерело; старі записи мають бути зупинені до видалення старого стовпця.
У термінах DDIA це еволюція коду й формату даних, що мусять певний час співіснувати. У термінах The Hard Parts це також рішення про межі даних і залежностей: зовнішній споживач старого поля або спільна транзакція змінюють вартість і черговість переходу. Жодна з цих ідей сама не визначає потрібне вікно чи інструмент; для цього треба знати власні споживачі й гарантії бази.
52.3. Операція DDL може бути короткою, а блокування — помітним
Зміна схеми впливає на паралельний доступ. У PostgreSQL 18 більшість форм ALTER TABLE за замовчуванням бере блокування ACCESS EXCLUSIVE, але для окремих підкоманд документація вказує слабший рівень. ACCESS EXCLUSIVE конфліктує з усіма режимами, включно з блокуванням, потрібним звичайному SELECT. Тому зміна лише в метаданих, яка сама виконується швидко, усе одно може чекати на попередню довгу транзакцію; поки вона чекає або тримає блокування, запити можуть затриматися. Перевіряйте конкретну підкоманду, транзакції та версію. (PostgreSQL 18, ALTER TABLE; Explicit Locking)
Важливо відрізняти «не переписує таблицю» від «не створює ризику для навантаження»:
- Додавання стовпця з фіксованим, не мінливим значенням за замовчуванням у PostgreSQL 18 може зберегти це значення в метаданих, не оновлюючи кожен старий рядок. Мінливе значення за замовчуванням може вимагати переписування.
- Зміна типу зазвичай переписує таблицю та індекси; деякі сумісні перетворення є винятком. Переписування великої таблиці може тривати довго й потребувати істотно більше тимчасового дискового простору.
- Звичайне створення індексу блокує записи в таблицю.
CREATE INDEX CONCURRENTLYдопускає звичайні записи, але робить два проходи, чекає на транзакції чи знімки, споживає ресурси й не може виконуватися всередині транзакційного блоку. Після невдачі може залишитися невалідний індекс, який треба перевірити й прибрати або перебудувати. - Перевірка нового обмеження може сканувати всі наявні рядки. У PostgreSQL
NOT VALIDдає змогу спочатку встановити деякі обмеження без перевірки старих рядків, але нові записи вже мають дотримуватися правила; окремийVALIDATE CONSTRAINTперевіряє старі рядки з іншим рівнем блокування.
Ці властивості специфічні для PostgreSQL 18 і точного виду операції. Суміжні підкоманди в одному ALTER TABLE можуть підвищити потрібний рівень блокування; інша база або інша версія може поводитися інакше. План не повинен обіцяти «нуль простою» лише тому, що команда називається concurrent або fast. (PostgreSQL 18, ALTER TABLE; CREATE INDEX; PostgreSQL 18, release notes)
Перед небезпечною DDL-операцією визначте допустимий час очікування на блокування й умови припинення спроби, перевірте відкриті транзакції, підготуйте повтор після тайм-ауту та узгодьте вікно з активністю системи. Переривання міграції не завжди повертає початковий стан: наприклад, конкурентне створення індексу може залишити об’єкт для окремого прибирання.
52.4. Заповнюйте великі таблиці порціями й перевіряйте інваріант
Backfill (пакетне заповнення) — це перенесення або обчислення нового значення для вже наявних рядків. Один великий UPDATE може довго тримати транзакцію, утворити значний журнал змін, затримати репліки, збільшити конкуренцію за блокування й навантажити пул з’єднань.
Для великого набору даних зазвичай планують обмежені порції:
- вибирайте стабільний ключ, наприклад первинний
id, і рухайте контрольну точку за ключем, а не черезOFFSET; - обмежуйте кількість рядків або тривалість однієї транзакції, фіксуйте результат і повторюйте;
- робіть операцію ідемпотентною: повторне виконання вже успішної порції не має псувати правильне значення;
- додавайте умову, яка захищає новіші записи, наприклад «заповнювати лише коли нове поле досі порожнє»;
- якщо джерельне поле може змінитися під час перенесення, синхронізуйте обидва значення в записі або зробіть повторний прохід для виявлення розбіжностей;
- фіксуйте контрольну точку лише після успішного запису порції; після збою відновлюйтеся з останньої зафіксованої межі;
- сповільнюйте порції, якщо зростають затримка запитів, очікування блокувань, використання з’єднань, журнал запису чи відставання реплік.
Контрольна точка підтверджує, де завершився процес, а не те, що результат правильний. Після перенесення перевірте кількість неповних рядків, помилок перетворення, дублікатів, різницю між джерельним і перетвореним значенням і бізнес-правило, яке тепер має забезпечувати схема. Де база це підтримує, краще перетворити важливий інваріант на обмеження. Звіт «оновлено 100% вибраних рядків» ще не доводить коректність перетворення.
У PostgreSQL обмеження CHECK виражає умову для рядка. Умова email_key IS NOT NULL відрізняється від email_key <> '': і порожній рядок, і NULL можуть мати різне бізнес-значення. Перевірте обидва випадки відповідно до контракту. Значення NULL проходить звичайну перевірку CHECK, якщо вираз повертає невідомий результат, тому перевірка лише email_key <> '' не замінює NOT NULL. (PostgreSQL 18, Constraints)
52.5. Три наскрізні навчальні сценарії
А. Безпечне перейменування contact_email
Зміна: стовпець contact_email має стати email_address. Просте RENAME COLUMN одразу прибере старе ім’я з-під чинного коду, звітів і черг. Якщо код ще звертається до нього, розгортання перерветься.
Послідовність переходу:
- Знайдіть усіх читачів і авторів: застосунок, працівників черги, SQL-звіти, експорт, тригери, подання й інтеграції. Перевірте, чи стовпець входить до індексу або обмеження.
- Додайте стовпець
email_address, який допускаєNULL. Старі екземпляри й далі використовуютьcontact_email. - Розгорніть сумісну версію коду. Поки нове поле не заповнене, читання може брати старе значення як запасне. Якщо сумісна версія пише в обидва стовпці, робіть це в одній транзакції та визначте, яке поле головне на кожній фазі.
- Заповніть
email_addressзі старого поля порціями. Цей перший прохід не є остаточною звіркою: поки старі автори працюють, вони можуть змінитиcontact_emailпісля обробки рядка. - Зупиніть старі процеси, відкладені повідомлення й звіти, що пишуть тільки в
contact_email. Поки старе поле лишається джерелом істини, повторно звірте всі рядки й виправте розбіжності вemail_address; перевірте, що їх не залишилося. Перед перемиканням відновіть потрібний тип,NOT NULL, індекси й інші обмеження так, щоб вони зберігали початковий контракт. - Перемкніть читання й запис на
email_address, зберігши старе поле на погоджений період сумісності та відновлення коду. - Лише після контрольної точки, коли всі споживачі перейшли на новий стовпець і перевірки пройшли, видаліть
contact_emailокремою міграцією.
Перевірка: жоден підтримуваний процес не читає або не змінює тільки старе поле; значення нового стовпця відповідають погодженій політиці; після перемикання немає старих записувачів. Якщо вимога лише змінити назву й усі споживачі можуть оновитися синхронно, пряме перейменування може бути доречним — але це інший набір передумов.
Б. Заповнення обов’язкового поля на великій таблиці
Зміна: таблиця customers має отримати normalized_code; за умовою прикладу кожен рядок цієї таблиці, незалежно від статусу клієнта, мусить мати нормалізований внутрішній код. У цьому синтетичному сценарії нормалізація прибирає крайні пробіли U+0020 і переводить у верхній регістр код із набору ASCII (American Standard Code for Information Interchange — американський стандартний код обміну інформацією). Допустимий вихідний формат після обрізання — від 1 до 32 символів: перший символ — латинська літера або цифра, наступні — латинські літери, цифри чи дефіси. Перевірка діапазонів символів і перетворення регістру можуть залежати від collation; у прикладах нижче явно задано collation C, щоб правило відповідало цьому набору. (PostgreSQL 18, Pattern Matching; Collation Support) Перед виконанням треба визначити, як виправляти старі рядки, де legacy_code відсутній або не відповідає формату.
Послідовність переходу:
- Додайте
normalized_codeз підтримкоюNULL. Переконайтеся, що стара версія може вставляти рядок, не знаючи про нове поле. - Розгорніть код, який перевіряє формат
legacy_codeі для кожного нового й оновленого клієнта записує узгодженіlegacy_codeтаnormalized_code. Якщо він оновлює старий рядок, деnormalized_codeщеNULL, він має заповнити його в тій самій операції. Для некоректного коду записувач має вимагати виправлення або відхилити зміну. - Дочекайтеся завершення розгортання та спорожнення черг: кожен активний записувач має створювати допустимий
legacy_codeі одночасно записувати відповіднийnormalized_code. Старі читачі можуть залишатися, але всі застосунки й прямі SQL-записувачі мають бути оновлені або зупинені. Інакше після додавання обмеження навіть звичайне оновлення старого рядка зnormalized_code IS NULLпорушить його, якщо записувач не заповнить нове поле.
ALTER TABLE customers
ADD CONSTRAINT customers_normalized_code_present
CHECK (normalized_code IS NOT NULL
AND (normalized_code COLLATE "C") ~ '^[A-Z0-9][A-Z0-9-]{0,31}$') NOT VALID;
- Заповнюйте старі рядки за діапазонами
id, невеликими транзакціями. Позначення:last_committed_idі:batch_end_idнижче символізують межі порції й залежать від інструмента виконання:
UPDATE customers
SET normalized_code = upper(btrim(legacy_code) COLLATE "C")
WHERE id > :last_committed_id
AND id <= :batch_end_id
AND normalized_code IS NULL
AND (btrim(legacy_code) COLLATE "C") ~ '^[A-Za-z0-9][A-Za-z0-9-]{0,31}$';
- Після кожної зафіксованої порції збережіть контрольну точку. Якщо процес перервався, повторно обробіть незавершену межу. Умову
IS NULLзалишайте лише якщо вона відповідає правилу перезапису; коли джерельний код змінюється паралельно, синхронізуйте обидва значення або виконайте звірку й повторний прохід. - Знайдіть рядки, які не відповідають визначеному формату, і виправте їх за погодженим продуктовим правилом. Оскільки остаточне обмеження поширюється на кожен рядок таблиці, не залишайте в ній винятків без значення; якщо рядок треба перенести чи вилучити, узгодьте це до міграції. Не підставляйте вигаданий код лише для проходження перевірки. Повторно обробіть виправлені рядки за їхніми
id, навіть якщо початкова контрольна точка вже пройшла їх. - Звірте кількість і значення, потім перевірте обмеження й встановіть остаточну гарантію
NOT NULL:
ALTER TABLE customers
VALIDATE CONSTRAINT customers_normalized_code_present;
ALTER TABLE customers
ALTER COLUMN normalized_code SET NOT NULL;
Синтаксис і блокування в прикладі стосуються PostgreSQL. NOT VALID пропускає початкову перевірку старих рядків, але не дозволяє новим або зміненим рядкам порушувати додане правило. Тому сумісний записувач має заповнювати normalized_code і під час оновлення старого неперенесеного рядка; інакше це оновлення відхилить обмеження. Пакетний запит навмисно пропускає рядки з відсутнім або некоректним вихідним кодом, доки їх не виправлено; потім виправлені рядки треба обробити окремо. Валідація перевіряє всі старі рядки, а встановлення NOT NULL завершує перехід. Перевірте сканування й блокування кожної операції за документацією своєї версії. Саме слово NOT VALID не означає «обмеження не діє».
Перевірка перед скороченням: кожен normalized_code відповідає формату обмеження; контрольні суми або вибіркові пари джерельних і нормалізованих значень узгоджені; нові записи проходять обмеження; старі версії коду вже зняті. Перевірте документацію обраної версії: наприклад, PostgreSQL 18 окремо додав підтримку NOT VALID для обмежень NOT NULL, але процедура й сумісність старіших версій не тотожні. (PostgreSQL 18, release notes)
В. Втрата точності під час перетворення грошей
Зміна: старий amount numeric(18,4) треба перетворити на суму з двома знаками після крапки. Це втрачає інформацію для значень із дробом у чотирьох знаках: наприклад, 12.3456 після округлення до двох знаків не дозволяє відновити первісне число. Крім точності, треба перевірити допустиму величину, валюту, момент округлення та правило для від’ємних значень.
Послідовність переходу:
- Погодьте доменне правило округлення для кожної валюти й перевірте діапазон нового типу. Не всі валюти мають дві мінорні десяткові позиції; «сума грошей» не визначає формат автоматично.
- Спершу додайте поле або таблицю-приймач відповідного типу. Запишіть старе точне значення, валюту й ключ рядка в обмежений за доступом журнал перетворення або збережіть старий стовпець. Визначте строк зберігання та захист цього чутливого набору.
- Для кожної порції обчисліть нове значення визначеною функцією, запишіть кількість округлених або переповнених рядків і зіставте підсумки за валютою. У PostgreSQL значення
numeric(p, s)приводиться до заданого масштабу з округленням; значенняnumeric, що лежать точно посередині між двома масштабами, округлюються від нуля;realіdouble precisionзазвичай округлюють такі половини до парного. Цей виняток не можна переносити на довільний тип чи базу. (PostgreSQL 18, Numeric Types) - Не перемикайте джерело істини, поки обчислення, переповнення, округлення, суми та вибіркові записи не перевірені власником правила.
- Після запису нових транзакцій у зменшеній точності повернення старої версії коду не відновить відкинуті дробові знаки. Для відновлення потрібні збережені початкові значення та визначена процедура звірки нових операцій; резервна копія чи журнал мають охоплювати потрібний момент.
Висновок: це потенційно незворотна зміна даних. Її можна спроєктувати так, щоб мати джерело відновлення, але down-міграція не є математичною оберненою функцією округлення.
52.6. Відкат коду не завжди відкочує дані
Відкат структури, коду й значень — різні дії. Видалення щойно доданого порожнього стовпця може бути оборотним; заповнення його похідними значеннями може бути оборотним лише коли зберегли правило та джерело; округлення, злиття дублікатів чи видалення рядків можуть втратити інформацію. Поведінка транзакцій DDL теж різниться між базами та операціями: перевірте, чи команда допускає транзакційний запуск і що лишається після помилки.
До запуску домовтеся про два шляхи:
- Відкотити код: нова версія не працює з продовженою схемою? Чи лишилися старі поля та формат читання, потрібні попередній версії? Чи сумісні нові записи зі старим читачем?
- Рухатися вперед: якщо дані вже перетворені, чи безпечніше виправити міграцію й повторити її, тимчасово вимкнути нову поведінку або компенсувати помилкове перетворення з журналу?
Заздалегідь визначте межу, після якої старий код не можна повертати. Якщо після переходу нова версія пише лише в нове поле, старий код може знову запрацювати технічно, але прочитати застаріле значення. Якщо видалили поле, його повернення не відновить стерті дані. Резервна копія й відновлення до попереднього моменту можуть скасувати всі наступні записи, тому разом із планом відновлення зафіксуйте спосіб зберегти чи повторити корисні нові зміни.
Документація Rails прямо зазначає, що деякі операції міграцій знищують дані й не можуть бути автоматично відмінені. Універсальна функція down не робить незворотний перерахунок оборотним. (Rails, Active Record Migrations: Reversible Migrations)
52.7. Реальні випадки: паралельні мігратори й ресурсний тиск
У 2023 році автор звернення golang-migrate № 960 описав збій із PostgreSQL 12 та golang-migrate 4.15.2: кілька екземплярів паралельно викликають m.Up() для міграції з CREATE INDEX CONCURRENTLY. За повідомленням, його тест проходив із паралельністю 1, але за більшої паралельності отримував взаємне блокування, помилку блокування pg_advisory_lock, «брудну» позначку версії міграції та невалідний індекс. Це конкретне відтворення автора для конкретної комбінації, а не доказ, що будь-який конкурентний індекс завжди створює таку помилку. На дату дослідження звернення відкрите й не містить підтвердженого виправлення в цьому проєкті; використовуйте його як сигнал перевірити серіалізацію запуску й очищення після невдалої спроби. (golang-migrate, issue #960; PostgreSQL 18, CREATE INDEX)
Ще один первинний звіт про блокування опублікувала інфраструктурна команда GitLab. Запис інциденту від 11 березня 2025 року повідомляє про насичення з’єднань PgBouncer до бази patroni-ci, довгі транзакції фонового працівника конвеєра — до 600 секунд — і скасування післярозгортальної міграції до пізнішого запуску. У пов’язаній коригувальній задачі команда пояснила, що довгі очікування блокування під піковим навантаженням можуть тримати чергу й з’єднання; вона обрала швидку невдалу спробу з повтором. У запиті на злиття (Merge Request, MR) !185888, об’єднаному 11 квітня 2025 року, довші тайм-аути очікування скоротили до 2 секунд, довгі паузи — до 15 секунд і збільшили кількість спроб, зберігши сумарне вікно повторів близько 40 хвилин. Це налаштування для GitLab, а не універсальні значення. Інцидент описує і довгі транзакції працівника, і міграцію; він не стверджує, що один SQL-запит був єдиною причиною. (GitLab, інцидент #19464; коригувальна задача #26379; MR !185888)
Окремий урок про потужність навантаження видно у звіті GitHub за травень 2026 року. Під час відновлювальних робіт після іншого інциденту GitHub запустив велику міграцію бази даних, що спричинила затримки реплікації. Захисний механізм сприйняв збільшене відставання як сигнал сповільнити записи; через це на певний час затрималися внутрішні події, які запускають обробку pull request і Copilot agent-сесії. Звіт повідомляє, що система відновилася після нормалізації відставання, та описує плани зробити фонову обробку стійкішою. Він не називає точного SQL чи блокування, тому це приклад впливу на залежні системи, а не доказ конкретної помилки запиту. У плані міграції стежте за реплікацією й чергами та оцінюйте вплив і під час відновлення після інших інцидентів. (GitHub Availability Report: May 2026)
Ці приклади показують різні механізми: паралельний запуск міграторів може конфліктувати з координацією, а тривала робота з даними може відчутно навантажити реплікацію й залежні сервіси. Вони не визначають одну універсальну стратегію. Перед запуском знайдіть відповідь на обидва питання: хто гарантує одиничний запуск потрібної міграції і якими сигналами система сповільнить або зупинить пакетну роботу?
52.8. План спостереження й критерій завершення
Перед міграцією запишіть очікувані передумови, контрольні точки, власника, залежні релізи та кроки відновлення. Під час зміни спостерігайте за:
- очікуванням блокувань, тайм-аутами й довгими транзакціями;
- тривалістю запиту, CPU (Central Processing Unit — центральним процесором), дисковим введенням-виведенням і простором для тимчасового переписування;
- кількістю оброблених рядків, помилками та контрольними точками backfill;
- затримкою реплік і черг, числом відкритих з’єднань та вільною місткістю пулу;
- помилками застосунку, часткою некоректних значень і порушеннями обмежень.
Зупинка має бути реальною дією: припинити видачу нових пакетів, безпечно завершити поточну транзакцію, зберегти позицію, визначити, які часткові об’єкти лишилися, і вирішити, чи повторювати з тієї самої версії. Значення порогів визначає власник системи за її звичайним рівнем і допустимою деградацією; ця глава не задає універсальної кількості секунд або відсотка навантаження.
Міграцію можна вважати завершеною, коли фактична структура відповідає очікуваній, потрібні дані пройшли незалежну перевірку, інваріанти зафіксовані або підтверджені, старі автори більше не змінюють попередній формат, а моніторинг не показує відкладених наслідків. Запис версії міграції — лише одна частина цього доказу.
Короткий підсумок
Міграція охоплює схему, дані, історію застосування й версії коду, що певний час співіснують. Розширюйте структуру до перемикання споживачів, переносіть старі значення контрольованими повторюваними порціями, перевіряйте інваріанти й скорочуйте сумісність лише після того, як старі читачі та автори виведені з роботи. Оцініть блокування, переписування, реплікацію, навантаження та втрату інформації для конкретної команди й версії бази. Оберіть відкат або рух уперед за тим, чи можна ще відновити попередній зміст даних.
Джерела та подальше читання
Матеріали перевірено 10 жовтня 2026 року. Документацію й поведінку бази слід звіряти з версією, драйвером, ORM (Object-Relational Mapping — зіставлення класів програми з реляційними таблицями) та фактичною конфігурацією перед практичним запуском.
- PostgreSQL 18, ALTER TABLE — режими блокування, переписування, значення за замовчуванням, зміна типу,
NOT VALIDі валідація обмежень. - PostgreSQL 18, Explicit Locking — конфлікти режимів блокування таблиць.
- PostgreSQL 18, CREATE INDEX — звичайне й конкурентне створення індексів, очікування та наслідки невдачі.
- PostgreSQL 18, Constraints — семантика
CHECK,NOT NULLта інших гарантій цілісності. - PostgreSQL 18, String Functions and Operators — символи, які
btrimприбирає за замовчуванням. - PostgreSQL 18, Pattern Matching і Collation Support — залежність діапазонів символів у регулярних виразах і перетворення регістру від collation.
- PostgreSQL 18, Numeric Types — точність, масштаб та округлення
numericі типів із рухомою комою. - PostgreSQL 18, Release Notes — версійні зміни підтримки
NOT VALIDдляNOT NULLта інші зміни DDL. - Ruby on Rails 7.2, Active Record Migrations — номер міграції в
schema_migrations, запуск за версіями й знімок структури. - Ruby on Rails, Active Record Migrations: Reversible Migrations — оборотні й незворотні операції.
- Martin Kleppmann, Chris Riccomini, Designing Data-Intensive Applications, 2nd edition, Chapter 5: Encoding and Evolution, O’Reilly, 2026 — зв’язок між зміною даних, схем і версій коду.
- Neal Ford, Mark Richards, Pramod Sadalage, Zhamak Dehghani, Software Architecture: The Hard Parts, Chapter 6: Pulling Apart Operational Data, O’Reilly, 2021 — компроміси меж даних, спільних транзакцій, зв’язків і поділу операційного сховища.
- golang-migrate, issue #960, 25 липня 2023 року — повідомлення й тест автора про взаємне блокування паралельних запусків міграції з конкурентним створенням індексу.
- GitLab, інцидент #19464, 11 березня 2025 року; коригувальна задача #26379, 11 березня 2025 року; запит на злиття !185888, об’єднаний 11 квітня 2025 року — насичення з’єднань, очікування блокувань і політика повтору.
- GitHub, Availability Report: May 2026, опублікований 11 червня 2026 року — опис впливу великої міграції на відставання реплік та залежну фонову обробку.