Як не вистрілити собі в ногу, використовуючи Liquibase

Ніколи не було, і знову!

На черговому проекті ми вирішили використати Liquibase із самого початку, щоб уникнути проблем у майбутньому. Як виявилося, не всі молоді члени команди вміють правильно його використовувати. Я провів внутрішній воркшоп, який потім вирішив перетворити на статтю.

Стаття включає корисні поради та опис трьох найбільш явних пасток, в які можна потрапити, працюючи з інструментами міграції реляційних баз даних, зокрема Liquibase. Розрахована на Java розробників рівня Junior та Middle, для більш досвідчених розробників може бути цікава для структуризації та повторення того, що, швидше за все, вже відомо.

Як не вистрілити собі в ногу, використовуючи Liquibase

Liquibase та Flyway — основні конкуруючі технології для вирішення задач контролю версій реляційних структур у світі Java. Перша є повністю безкоштовною, на практиці найчастіше вибирається для використання саме вона, тому саме Liquibase обрано героєм публікації. Тим не менш, деякі з описаних практик можуть бути універсальними, в залежності від архітектури вашого додатку.

Міграції реляційних структур – це вимушений спосіб боротьби зі слабкою гнучкістю реляційних сховищ даних. В епоху моди на ОВП стиль роботи з БД мав на увазі, що ми одного разу опишемо схему і не будемо її більше чіпати. Але реальність завжди така, що все змінюється, і зміни у структурі таблиць потрібні досить часто. Звісно, ​​процес сам собою буває болючий і неприємний.

Не заглиблюватимусь в опис технології та інструкції з додавання бібліотеки до свого проекту, на цю тему було написано достатньо статей:

Крім того, вже була чудова стаття на тему корисних порад:

Поради

Хочу поділитися своїми порадами та коментарями, які народилися через піт, кров та біль вирішення проблем із міграцією.

1. Перед роботою слід ознайомитися з розділом кращих практик сайті Liquibase

Там описано прості, але дуже важливі речі, без яких використання бібліотеки може ускладнити вам життя. Наприклад, неструктурний підхід до управління ченджсетами рано чи пізно призведе до плутанини та поламаних міграцій. Якщо викочувати зміни структури БД і логіки сервісів, що залежать один від одного, не одночасно, тобто велика ймовірність, що це призведе до червоних тестів або поламаного оточення. Крім того, рекомендації щодо використання Liquibase на офіційному сайті містять пункт про розробку та перевірку rollback скриптів разом із основними скриптами міграції. Ну і у статті https://habr.com/ru/post/178665/ Існують приклади коду, що стосується міграцій і rollback механізму.

2. Якщо почали використовувати засоби міграції – не допускайте мануальних виправлень у структурі бази

Як говориться: «Один раз Persil завжди Persil». Якщо база вашої програми почала керуватися засобами Liquibase - будь-які ручні зміни моментально призводять до неконсистентного стану, і рівень довіри ченджсетам стає нульовим. Потенційні ризики — кілька витрачених годин на відновлення бази, за найгіршого розкладу — вбитий сервер. Якщо у вашій команді є DBA Architect «старого гарту», ​​терпляче і вдумливо поясніть йому, як все буде погано, якщо він просто відредагує базу за своїм розумінням з умовного SQL Developer.

3. Якщо ченджсет вже був запущений до репозиторію, уникайте редагування

Якщо інший розробник зробив pull і застосував ченджсет, який згодом буде відредагований, він обов'язково згадає вас добрим словом, коли отримає помилку при старті програми. Якщо редагування ченджсета якимось чином протікає в девелоп - доведеться йти слизькою доріжкою хотфіксів. Суть проблеми впирається у валідацію змін щодо хеш-суми – основний механізм Liquibase. Під час редагування коду ченджсета змінюється хеш-сума. Редагування ченджсетів можливе лише тоді, коли є можливість без втрати даних розгорнути всю базу з нуля. У такому разі рефакторинг SQL або XML коду може, навпаки, полегшити життя, зробити міграції більш читаними. Прикладом може бути ситуація, коли на старті програми схема вихідної БД узгоджувалась усередині команди.

4. Май перевірені бекапи баз даних, якщо це можливо

Тут, гадаю, все зрозуміло. Якщо міграція пройшла невдало, все можна буде повернути назад. У Liquibase є інструмент відкату змін, але скрипти для відкату пише теж сам розробник, і в них можуть бути проблеми з такою самою ймовірністю, як і в скриптах основного ченджсета. Це означає, що перестрахування з бекапами корисно в будь-якому випадку.

5. Використовуй перевірені бекапи баз даних у розробці, якщо це можливо

Якщо це не суперечить договорам і privacy, в базі немає персональних даних, і вона не важить як два сонця - перед застосуванням на живих серверах міграції можна перевірити, як воно спрацює на машині розробника, і вирахувати майже 100% потенційних проблем при міграції.

6. Спілкуйся з іншими розробниками у команді

У правильно організованому процесі розробки усі в команді знають, хто чим зайнятий. Насправді часто це не так, тому якщо в рамках свого завдання готуєш зміни в структурі БД, бажано додатково сповістити про це всю команду. Якщо хтось робить зміни паралельно, вам слід акуратно організуватися. З колегами варто спілкуватися і після завершення роботи, не лише на старті. Багато потенційних проблем із ченджсетами може вирішитися на етапі code review.

7. Думай, що робиш!

Здавалося б, самоочевидна рада, яка застосовується до будь-яких ситуацій. Однак багатьох проблем вдалося б уникнути, якби розробник вкотре проаналізував, що він робить і на що це може вплинути. Робота з міграціями завжди потребує додаткової уваги та акуратності.

пастки

Давайте тепер розглянемо типові пастки, в які можна потрапити, якщо не слідувати порадам вище, і що, власне, робити?

Ситуація 1. Два розробники намагаються одночасно додавати нові ченджсети

Як не вистрілити собі в ногу, використовуючи Liquibase
Вася та Петя хочуть створити ченджсет версії 4, не знаючи один про одного. Вони змінили структуру БД, і викотили pull request, з різними файлами ченджсета. Далі пропонується наступний механізм дій:

Як вирішувати

  1. Якимось чином колеги повинні домовитися, в якому порядку повинні йти їхні ченджсети, скажімо, Петін має бути застосований перший.
  2. Хтось один повинен підлити другий до себе та позначити ченджсет Васі версією 5. Це може бути зроблено через Cherry Pick чи акуратний мердж.
  3. Після змін обов'язково слід перевірити валідність вчинених дій.
    Насправді механізми Liquibase дозволять мати у репозиторії два ченджсети версії 4, тому можна залишити все як є. Тобто у вас просто буде дві зміни версії 4 із різними назвами. За такого підходу згодом у версіях бази даних стає дуже складно орієнтуватися.

Крім того, Liquibase, як будинки хобітів, зберігає в собі багато секретів. Одним із них є ключ validCheckSum, який з'явився з версії 1.7 і дозволяє вказати валідне значення хеш-суми для певного ченджсета незалежно від того, що зберігається в базі даних. Документація https://www.liquibase.org/documentation/changeset.html каже таке:

Add a checksum that is considered valid for this changeSet, незважаючи на те, що це stored in the database. Використовувалися в першу чергу, коли вам потрібна зміна зміниSet and don't want errors thrown on databases on which it has already run (no recomended procedure)

Так, така процедура не рекомендується. Але іноді сильний світлий маг володіє і темними техніками.

Ситуація 2. Міграція, яка залежить від даних

Як не вистрілити собі в ногу, використовуючи Liquibase

Припустимо, у вас немає можливості використовувати резервні копії баз із живих серверів. Петя створив ченджсет, перевірив його локально і з упевненістю своєї правоти зробив pull request в девелоп. Лід проекту про всяк випадок уточнив, чи перевірив його Петя, а потім влив. Але розгортання на девелоп-сервері впало.

Насправді таке можливе і від цього ніхто не застрахований. Це відбувається у разі, якщо модифікації структури таблиць якимось чином пов'язані на конкретні дані з БД. Очевидно, що якщо база Петі заповнена лише тестовими даними, вона може не покривати всі проблемні кейси. Наприклад, при видаленні таблиці з'ясовується, що є записи в інших таблицях Foreign Key, пов'язані з записами в видаляється. Або при зміні типу колонки з'ясовується, що не 100% даних можуть бути перетворені на новий тип.

Як вирішувати

  • Написати спеціальні скрипти, які одноразово застосовуватимуться разом із міграцією та наводитимуть дані у належний вигляд. Це загальний шлях розв'язання проблеми перенесення даних у нові структури вже після застосування міграцій, але щось подібне може бути застосовано і до, в окремих випадках. Такий шлях, звичайно, не завжди доступний, бо редагувати дані на живих серверах може бути небезпечно і навіть згубно.
  • Інший складний шлях - відредагувати існуючий ченджсет. Складність у цьому, що це БД, де він у вигляді вже був застосований, доведеться відновлювати. Цілком можливо, що вся бекенд-команда змушена буде локально накотити БД з нуля.
  • І найуніверсальніший шлях - перенесення проблеми з даними на environment розробника з відтворенням тієї ж ситуації та додавання нового ченджсета, до зламаного, який дозволить оминути проблему.
    Як не вистрілити собі в ногу, використовуючи Liquibase

В цілому, чим більше база за складом даних схожа на базу продакшн сервера, тим менший шанс, що проблеми з міграціями протікають далеко. І, звичайно, перш ніж відправити ченджсет до репозиторію, варто кілька разів подумати, чи не зламає він щось.

Ситуація 3. Liquibase починає застосовуватися після виходу в продакшн

Припустимо, тимлід попросив Петю підключити до проекту Liquibase, проте проект вже у продакшені і вже є існуюча структура бази.

Відповідно, проблема полягає в тому, щоб на будь-яких нових серверах або машинах розробників дані таблиці повинні відтворюватися з нуля, а існуюче середовище має залишитися в консистентному стані, будучи готовою приймати нові ченджсети.

Як вирішувати

Тут також є кілька шляхів:

  • Перший і найочевидніший — мати окремий скрипт, який можна застосувати вручну при ініціалізації нового оточення.
  • Другий – менш очевидний, мати Liquibase міграцію, яка знаходиться в іншому Liquibase Context, та застосовувати її. Докладніше про Liquibase Context можна прочитати тут: https://www.liquibase.org/documentation/contexts.html. Загалом це цікавий механізм, який може бути успішно застосований, наприклад, для тестування.
  • Третій шлях складається з кількох кроків. Спочатку має бути створена міграція для вже наявних таблиць. Потім вона повинна бути застосована на якомусь environment'і і таким чином буде отримана її хеш-сума. Наступним кроком слід проініціалізувати на нашому не порожньому сервері порожні Liquibase таблиці, і в таблицю з історією застосування ченджсетів можна мануально покласти запис про «ніби застосований» ченджсет з вже наявними в базі змінами. Таким чином, на вже існуючому сервері відлік історії піде з версії 2, а всі нові environments будуть вести себе ідентично.
    Як не вистрілити собі в ногу, використовуючи Liquibase

Ситуація 4. Міграції стають величезними та не встигають виконуватися

На початку розробки сервісу, як правило, Liquibase використовується як зовнішня залежність і всі міграції обробляються при старті програми. Однак згодом ви можете натрапити на такі кейси:

  • Міграції стають величезними та виконуються довгий час.
  • З'являється необхідність міграції в розподілених середовищах, скажімо, на кількох інстансах серверів БД одночасно.
    У такому разі занадто тривале застосування міграцій призведе до таймуту при старті програми. Крім того, застосування міграцій для кожного інстансу програми окремо може призвести до того, що різні сервери виявляться в не синхронному стані.

Як вирішувати

У таких випадках ваш проект вже великий, можливо, навіть повнолітній, і Liquibase починає виступати як окремий зовнішній інструмент. Справа в тому, що Liquibase як бібліотека збирається в jar файл, і може працювати як залежність усередині проекту, так і автономно.

В автономному режимі можна покласти застосування міграцій на ваше CI/CD середовище або на міцні плечі ваших системних адміністраторів з розгортання. Для цього знадобиться командний рядок Liquibase https://www.liquibase.org/documentation/command_line.html. У такому режимі з'являється можливість здійснювати запуск програми після того, як всі необхідні міграції були проведені.

Висновок

Насправді пасток при роботі з міграціями БД може бути набагато більшим, і багато з них вимагають творчого підходу. Важливо розуміти, що якщо правильно використовувати інструмент, більшість з цих пасток вдасться уникнути. Саме мені доводилося в різних видах стикатися з усіма перерахованими проблемами, а деякі з них були результатом моїх косяків. В основному це відбувається, звичайно, через неуважність, але іноді – через злочинне невміння використовувати інструмент.

Джерело: habr.com

Додати коментар або відгук