Как да не си простреляте крака, използвайки 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 архитект от старата школа, търпеливо и замислено му обяснете как всичко ще бъде зле, ако просто редактira базата по свое усмотрение от условния SQL Developer.

3. Ако ченджсетът вече е бил пуснат в репозитория, избягвайте редактиране

Ако друг разработчик е направил pull и е приложил ченджсет, който по-късно ще бъде редактиран, — той задължително ще помисли за вас с добри думи, когато получи грешка при стартиране на приложението. Ако редактирането на ченджсета по някакъв начин премине в девелоп — ще се наложи да се поеме по хлъзгавия път на хотфиксовете. Същността на проблема опира до валидирането на промените по хеш-сумата — основният механизъм на Liquibase. При редактиране на кода на ченджсета се променя хеш-сумата. Редактирането на ченджсетовете е възможно само когато има възможност без загуба на данни да се разпредели цялата база отначало. В такъв случай, рефакторингът на SQL или XML кода може, всъщност, да улесни живота, правейки миграциите по-четими. Пример за това може да бъде ситуация, при която при стартиране на приложението схемата на изходната БД е била съгласувана вътре в екипа.

4. Имай проверени бекъпи на бази данни, ако е възможно

Тук, мисля, всичко е ясно. В случай че миграцията не успее, всичко може да бъде възстановено. В Liquibase има инструмент за отмяна на промените, но скриптовете за отмяна също се пишат от разработчика, и в тях могат да възникнат проблеми с такава съща вероятност, както и в основните скриптове на промените. Това означава, че е полезно да се правят бекъпи във всички случаи.

5. Използвай проверени бекъпи на бази данни в разработката, ако е възможно

Ако това не противоречи на договорите и личните данни, и в базата няма чувствителна информация, и тя не тежи колкото две слънца — преди да извършиш миграции на живи сървъри, можеш да провериш как ще се получи на машината на разработчика и да изчислиш почти 100% от потенциалните проблеми при миграцията.

6. Общувай с другите разработчици в екипа

В добре организиран процес на разработка всеки в екипа знае кой с какво се занимава. В действителност, често не е така, затова, ако по твоята задача подготвяш промени в структурата на базата данные, би било добре да уведомиш целия екип. Ако някой прави промени паралелно — трябва да се организирате внимателно. С колегите е добре да общуваш и след завършване на работата, а не само в началото. Много потенциални проблеми с промените могат да се разрешат на етапа на преглед на кода.

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 казва следното:

Добавете хеш-сметка, която се счита за валидна за този changeSet, независимо от това, което е съхранено в базата данни. Използва се основно, когато трябва да промените changeSet и не искате да получите грешки на бази данни, на които вече е работил (непрепоръчвана процедура).

Да-да, такава процедура не се препоръчва. Но понякога мощен светъл маг притежава и тъмни техники.

Ситуация 2. Миграция, която зависи от данните.

Как да не си простреляте крака, използвайки Liquibase

Предположим, че нямате възможност да използвате резервни копия на базите от живи сървъри. Петя създаде ченджсет, провери го локално и с пълна увереност в правотата си направи pull request в девелоп. Лидерът на проекта за всеки случай уточни, провери ли го Петя, и след това го включи. Но разгръщането на девелоп сървъра се провали.

Всъщност това е възможно и никой не е застрахован от него. Това се случва в случай, че модификациите на структурата на таблиците по някакъв начин зависят от конкретни данни от БД. Очевидно е, че ако базата на Петя е запълнена само с тестови данни, тя може да не покрива всички проблемни случаи. Например, при изтриването на таблица се установява, че има записи в други таблици по Foreign Key, свързани с записите в изтритата. Или при промяна на типа колона, се установява, че не 100% от данните могат да бъдат преобразувани в новия тип.

Как да действаме

  • Напишете специализирани скриптове, които ще се прилагат веднъж заедно с миграцията и ще приведат данните в необходимия вид. Това е общ подход за решаване на проблема с прехвърлянето на данни в нови структури след прилагането на миграциите, но подобно нещо може да се приложи и преди, в специфични случаи. Такъв вариант, разбира се, не винаги е достъпен, тъй като редактиране на данни на активни сървъри може да бъде опасно и дори вредно.
  • Другият сложен начин е да се редактира съществуващият changset. Сложността е в това, че всички бази данни, в които е бил приложен в настоящия си вид, трябва да се възстановят. Възможно е целият бекенд екип да бъде принуден да локално инсталира базата данни от нулата.
  • И най-универсалният начин е да се прехвърли проблемът с данните в средата на разработчика, създавайки същата ситуация и добавяйки нов changset, преди счупения, който ще позволи да се избегне проблемът.
    Как да не си простреляте крака, използвайки Liquibase

Общо взето, колкото повече базата данни по състав данни наподобява базата на производствения сървър, толкова по-малка е вероятността проблемите с миграциите да протекат дълбоко. И разбира се, преди да се изпрати changset в хранилището, си струва няколко пъти да се замислите дали няма да счупи нещо.

Ситуация 3. Liquibase започва да се прилага вече след излизането в продукция

Предположим, че тимлидът е помолил Петя да свърже Liquibase с проекта, но проектът вече е в продукция и съществуваща структура на базата вече е налична.

Съответно, проблемът се състои в това, че на каквито и да е нови сървъри или машини на разработчиците, данните в таблиците трябва да се възстановят от нулата, а съществуващата среда трябва да остане в консистентно състояние, готова да приема нови changsets.

Как да действаме

Тук също има няколко пътя:

  • Първият и най-очевидният е да имате отделен скрипт, който трябва да се приложи ръчно при инициализацията на новата среда.
  • Вторият е по-малко очевиден - да имате Liquibase миграция, която се намира в друг Liquibase контекст и да я приложите. По-подробно за Liquibase контекст може да прочетете тук: https://www.liquibase.org/documentation/contexts.html. В общи линии, това е интересен механизъм, който може успешно да се прилага, например, за тестване.
  • Третият път се състои от няколко стъпки. Първо трябва да бъде създадена миграция за вече съществуващите таблици. След това тя трябва да бъде приложена върху определена среда, с което ще бъде получена нейната хеш-сумма. Следващата стъпка е да се инициализират на нашия не-празен сървър празни таблици на Liquibase, и в таблицата с историята на прилагане на ченджсетите може ръчно да бъде добавена запис за „както че е приложен“ ченджсет с вече съществуващите в базата изменения. По този начин, на вече съществуващия сървър, историята ще започне от версия 2, а всички нови среди ще се държат идентично.
    Как да не си простреляте крака, използвайки Liquibase

Ситуация 4. Миграциите стават огромни и не успяват да бъдат изпълнени.

В началото на разработката на услугата, обикновено Liquibase се използва като външна зависимост и всички миграции се обработват при стартиране на приложението. Въпреки това, с времето може да се сблъскате с следните случаи:

  • Миграциите стават огромни и отнемат дълго време за изпълнение.
  • Появява се необходимост от миграция в разпределени среди, например на няколко инстанции на бази данни едновременно.
    В такъв случай, прекалено дългото прилагане на миграциите ще доведе до таймаут при стартиране на приложението. Освен това, прилагането на миграции за всеки инстанс на приложението поотделно може да доведе до несинхронизация между различните сървъри.

Как да действаме

В такива случаи вашият проект вече е голям, може би дори е достигнал зрялост, и Liquibase започва да функционира като отделен външен инструмент. Работата е там, че Liquibase като библиотека се компилира в jar файл и може да работи както като зависимост във вътрешността на проекта, така и самостоятелно.

В самостоятелен режим може да се възложи прилагането на миграции на вашата CI/CD среда или на опитните ръце на вашите системни администратори/специалисти по разгръщане. За това ще е нужно командния ред на Liquibase. https://www.liquibase.org/documentation/command_line.htmlВ такъв режим се появява възможността да стартирате приложението вече след като всички необходими миграции са проведени.

Извод

Всъщност капаните при работа с миграции на БД могат да бъдат много повече и много от тях изискват креативен подход. Важно е да разберем, че ако инструментът се използва правилно, повечето от тези капани могат да бъдат избегнати. Лично на мен ми се е налагало да се сблъсквам с всички изброени проблеми в различни форми, а някои от тях бяха резултат на мои грешки. Основно това се случва, разбира се, по невнимание, но понякога – поради престъпно неумение за работа с инструмента.

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster