Jak uniknąć strzelenia sobie w stopę stosując Liquibase

Nigdy wcześniej się to nie zdarzyło, a teraz wracamy!

Przy kolejnym projekcie postanowiliśmy od początku zastosować Liquibase, aby uniknąć problemów w przyszłości. Jak się okazuje, nie wszyscy młodzi członkowie zespołu wiedzą, jak poprawnie z niego korzystać. Poprowadziłem wewnętrzne warsztaty, które następnie postanowiłem przekształcić w artykuł.

W artykule znajdują się przydatne wskazówki oraz opis trzech najbardziej oczywistych pułapek, na które można wpaść podczas pracy z narzędziami do migracji relacyjnych baz danych, w szczególności Liquibase. Zaprojektowany dla programistów Java na poziomach Junior i Middle; dla bardziej doświadczonych programistów może być interesujący do strukturyzacji i powtarzania tego, co najprawdopodobniej już jest znane.

Jak uniknąć strzelenia sobie w stopę stosując Liquibase

Liquibase i Flyway to główne konkurencyjne technologie rozwiązujące problemy kontroli wersji struktur relacyjnych w świecie Java. Ten pierwszy jest całkowicie darmowy, w praktyce najczęściej wybierany do użytku, dlatego też na bohatera publikacji wybrano Liquibase. Jednakże niektóre z opisanych praktyk mogą być uniwersalne, w zależności od architektury aplikacji.

Migracje struktur relacyjnych są wymuszonym sposobem radzenia sobie ze słabą elastycznością relacyjnych magazynów danych. W dobie mody OOP styl pracy z bazami danych powodował, że schemat opisywaliśmy raz i nie ruszaliśmy go ponownie. Rzeczywistość jest jednak taka, że ​​wszystko się zmienia i zmiany w strukturze tabeli są wymagane dość często. Naturalnie sam proces może być bolesny i nieprzyjemny.

Nie będę wnikał głębiej w opis technologii i instrukcje dodania biblioteki do projektu, napisano na ten temat sporo artykułów:

Poza tym był już doskonały artykuł na temat przydatnych wskazówek:

Советы

Chcę podzielić się radami i uwagami, które zrodziły się w pocie, krwi i bólu rozwiązywania problemów migracyjnych.

1. Przed przystąpieniem do pracy warto zapoznać się z sekcją dobrych praktyk dot witryna internetowa Likwibaza

Там Opisane są proste, ale bardzo istotne rzeczy, bez których korzystanie z biblioteki może skomplikować życie. Na przykład nieustrukturyzowane podejście do zarządzania zestawami zmian prędzej czy później doprowadzi do zamieszania i przerwania migracji. Jeśli nie wprowadzisz jednocześnie wzajemnie zależnych zmian w strukturze bazy danych i logice usługi, istnieje duże prawdopodobieństwo, że doprowadzi to do czerwonych testów lub uszkodzonego środowiska. Ponadto zalecenia dotyczące korzystania z Liquibase na oficjalnej stronie internetowej zawierają klauzulę dotyczącą opracowywania i testowania skryptów przywracania zmian wraz z głównymi skryptami migracji. No cóż, w artykule https://habr.com/ru/post/178665/ Istnieją przykłady kodu dotyczące migracji i mechanizmu wycofywania zmian.

2. Jeśli zaczniesz korzystać z narzędzi migracyjnych, nie zezwalaj na ręczne poprawki w strukturze bazy danych

Jak to się mówi: „Raz Persil, zawsze Persil”. Jeśli podstawą Twojej aplikacji zacznie być zarządzana Liquibase, wszelkie ręczne zmiany natychmiast doprowadzą do niespójnego stanu, a poziom zaufania do zestawów zmian spadnie do zera. Potencjalne ryzyko obejmuje kilka godzin spędzonych na przywracaniu bazy danych; w najgorszym przypadku martwy serwer. Jeśli masz w swoim zespole architekta DBA ze starej szkoły, cierpliwie i z namysłem wyjaśnij mu, jak źle będzie, jeśli po prostu zmodyfikuje bazę danych zgodnie z własnym zrozumieniem od programisty warunkowego SQL.

3. Jeśli zestaw zmian został już wypchnięty do repozytorium, unikaj edycji

Jeśli inny programista wykonał ściąganie i zastosował zestaw zmian, który później będzie edytowany, na pewno zapamięta Cię miłym słowem, gdy otrzyma błąd podczas uruchamiania aplikacji. Jeśli edycja zestawu zmian w jakiś sposób przedostanie się do środowiska programistycznego, będziesz musiał podążać za krętymi ścieżkami poprawek. Istota problemu polega na walidacji zmian poprzez sumę skrótów – główny mechanizm Liquibase. Podczas edycji kodu zestawu zmian zmienia się wartość skrótu. Edycja zestawów zmian jest możliwa tylko wtedy, gdy możliwe jest wdrożenie całej bazy danych od zera bez utraty danych. W tym przypadku refaktoryzacja kodu SQL lub XML może wręcz przeciwnie ułatwić życie i sprawić, że migracje będą bardziej czytelne. Przykładem może być sytuacja, gdy na początku aplikacji został uzgodniony w zespole schemat źródłowej bazy danych.

4. Jeśli to możliwe, zweryfikuj kopie zapasowe bazy danych

Tutaj myślę, że wszystko jest jasne. Jeśli nagle migracja się nie powiedzie, wszystko można zwrócić. Liquibase posiada narzędzie do wycofywania zmian, jednak skrypty wycofywania zmian są również pisane przez samego programistę i mogą powodować problemy z takim samym prawdopodobieństwem, jak skrypty głównego zestawu zmian. Oznacza to, że w każdym przypadku warto zachować ostrożność, tworząc kopie zapasowe.

5. Jeśli to możliwe, korzystaj ze sprawdzonych kopii zapasowych baz danych podczas programowania

Jeśli nie stoi to w sprzeczności z umowami i prywatnością, w bazie nie ma danych osobowych, a ona nie waży aż dwóch słońc – przed użyciem na serwerach migracyjnych na żywo można sprawdzić, jak będzie działać na maszynie dewelopera i obliczyć prawie 100% potencjalnych problemów podczas migracji.

6. Komunikuj się z innymi programistami w zespole

W dobrze zorganizowanym procesie rozwoju każdy w zespole wie, kto co robi. W rzeczywistości często tak nie jest, dlatego jeśli w ramach swojego zadania przygotowujesz zmiany w strukturze bazy danych, warto dodatkowo powiadomić o tym cały zespół. Jeśli ktoś wprowadza zmiany równolegle, należy to starannie zorganizować. Warto komunikować się ze współpracownikami po zakończeniu pracy, a nie tylko na początku. Wiele potencjalnych problemów ze zbiorami zmian można rozwiązać na etapie przeglądu kodu.

7. Pomyśl o tym, co robisz!

Wydawałoby się, że jest to oczywista rada, która ma zastosowanie w każdej sytuacji. Wielu problemów można było jednak uniknąć, gdyby programista jeszcze raz przeanalizował, co robi i na co może to wpłynąć. Praca z migracjami zawsze wymaga dodatkowej uwagi i dokładności.

Pułapki

Przyjrzyjmy się teraz typowym pułapkom, w które możesz wpaść, jeśli nie zastosujesz się do powyższych rad i co dokładnie powinieneś zrobić?

Sytuacja 1: Dwóch programistów próbuje dodać nowe zestawy zmian w tym samym czasie

Jak uniknąć strzelenia sobie w stopę stosując Liquibase
Vasya i Petya chcą stworzyć zestaw zmian w wersji 4, nie wiedząc o sobie nawzajem. Wprowadzili zmiany w strukturze bazy danych i wydali żądanie ściągnięcia z różnymi plikami zestawu zmian. Proponuje się następujący mechanizm działania:

Jak zdecydować

  1. W jakiś sposób koledzy muszą uzgodnić kolejność, w jakiej mają zostać wprowadzone ich zestawy zmian, na przykład Petin powinien zostać zastosowany jako pierwszy.
  2. Ktoś powinien dodać do siebie drugi i oznaczyć zestaw zmian Vasyi wersją 5. Można to zrobić poprzez Cherry Pick lub zgrabne połączenie.
  3. Po zmianach zdecydowanie warto sprawdzić zasadność podjętych działań.
    Tak naprawdę mechanizmy Liquibase pozwolą Ci mieć w repozytorium dwa zestawy zmian wersji 4, więc możesz zostawić wszystko bez zmian. Oznacza to, że w wersji 4 będą po prostu dwie zmiany o różnych nazwach. Dzięki takiemu podejściu nawigacja po wersjach bazy danych staje się później bardzo trudna.

Ponadto Liquibase, podobnie jak dom hobbitów, skrywa wiele tajemnic. Jednym z nich jest klucz validCheckSum, który pojawił się w wersji 1.7 i pozwala określić prawidłową wartość skrótu dla konkretnego zestawu zmian, niezależnie od tego, co jest przechowywane w bazie danych. Dokumentacja https://www.liquibase.org/documentation/changeset.html mówi co następuje:

Dodaj sumę kontrolną uznawaną za prawidłową dla tego zestawu zmian, niezależnie od tego, co jest przechowywane w bazie danych. Używane głównie wtedy, gdy musisz zmienić zestaw zmian i nie chcesz, aby błędy były zgłaszane w bazach danych, w których został on już uruchomiony (nie jest to zalecana procedura)

Tak, tak, ta procedura nie jest zalecana. Ale czasami silny mag światła opanowuje także ciemne techniki

Sytuacja 2: Migracja zależna od danych

Jak uniknąć strzelenia sobie w stopę stosując Liquibase

Załóżmy, że nie masz możliwości korzystania z kopii zapasowych baz danych z działających serwerów. Petya stworzył zestaw zmian, przetestował go lokalnie i mając całkowitą pewność, że ma rację, wysłał żądanie ściągnięcia do programisty. Na wszelki wypadek kierownik projektu wyjaśnił, czy Petya to sprawdził, a następnie dodał. Jednak wdrożenie na serwerze programistycznym spadło.

W rzeczywistości jest to możliwe i nikt nie jest na to odporny. Dzieje się tak, jeśli modyfikacje struktury tabeli są w jakiś sposób powiązane z konkretnymi danymi z bazy danych. Oczywiście, jeśli baza danych Petyi jest wypełniona wyłącznie danymi testowymi, może nie obejmować wszystkich problematycznych przypadków. Przykładowo przy usuwaniu tabeli okazuje się, że w innych tabelach według klucza obcego znajdują się rekordy powiązane z rekordami w usuwanej tabeli. Lub przy zmianie typu kolumny okazuje się, że nie 100% danych da się przekonwertować na nowy typ.

Jak zdecydować

  • Napisz specjalne skrypty, które zostaną użyte jednorazowo przy migracji i doprowadzą dane do odpowiedniej postaci. Jest to ogólny sposób rozwiązania problemu przenoszenia danych do nowych struktur po zastosowaniu migracji, ale w szczególnych przypadkach można zastosować wcześniej coś podobnego. Ścieżka ta oczywiście nie zawsze jest dostępna, gdyż edycja danych na działających serwerach może być niebezpieczna, a nawet destrukcyjna.
  • Innym trudnym sposobem jest edycja istniejącego zestawu zmian. Trudność polega na tym, że wszystkie bazy danych, w których został on już zastosowany w dotychczasowej formie, będą musiały zostać przywrócone. Całkiem możliwe, że cały zespół backendowy będzie zmuszony lokalnie wdrożyć bazę danych od zera.
  • A najbardziej uniwersalnym sposobem jest przeniesienie problemu z danymi do środowiska deweloperskiego, odtworzenie tej samej sytuacji i dodanie do uszkodzonego nowego zestawu zmian, co obejdzie problem.
    Jak uniknąć strzelenia sobie w stopę stosując Liquibase

Ogólnie rzecz biorąc, im bardziej baza danych jest podobna w składzie do bazy danych serwera produkcyjnego, tym mniejsze ryzyko, że problemy z migracjami będą sięgać daleko. I oczywiście zanim wyślesz zestaw zmian do repozytorium, powinieneś kilka razy pomyśleć, czy coś to zepsuje.

Sytuacja 3. Liquibase zaczyna być używany po wejściu do produkcji

Załóżmy, że lider zespołu poprosił Petyę o włączenie Liquibase do projektu, ale projekt jest już w fazie produkcyjnej i istnieje istniejąca struktura bazy danych.

W związku z tym problem polega na tym, że na każdym nowym serwerze lub komputerze programisty tabele te muszą zostać utworzone od podstaw, a istniejące środowisko musi pozostać w spójnym stanie, gotowym na przyjęcie nowych zestawów zmian.

Jak zdecydować

Istnieje również kilka sposobów:

  • Pierwszą i najbardziej oczywistą jest posiadanie osobnego skryptu, który należy zastosować ręcznie podczas inicjowania nowego środowiska.
  • Drugie rozwiązanie jest mniej oczywiste, należy przeprowadzić migrację Liquibase w innym kontekście Liquibase i zastosować ją. Więcej o kontekście Liquibase możesz przeczytać tutaj: https://www.liquibase.org/documentation/contexts.html. Generalnie jest to ciekawy mechanizm, który z powodzeniem można wykorzystać np. do testów.
  • Trzecia ścieżka składa się z kilku kroków. Najpierw należy utworzyć migrację dla istniejących tabel. Następnie należy go zastosować do jakiegoś środowiska i w ten sposób uzyskana zostanie jego suma skrótu. Następnym krokiem jest inicjalizacja pustych tabel Liquibase na naszym niepustym serwerze i w tabeli z historią użycia zestawów zmian można ręcznie umieścić zapis o zestawie zmian „jakby zastosowany” ze zmianami już istniejącymi w bazie danych . Tym samym na istniejącym serwerze odliczanie historii rozpocznie się od wersji 2, a wszystkie nowe środowiska będą zachowywać się identycznie.
    Jak uniknąć strzelenia sobie w stopę stosując Liquibase

Sytuacja 4. Migracje stają się ogromne i nie mają czasu na ich ukończenie

Na początku rozwoju usługi z reguły Liquibase jest używany jako zależność zewnętrzna, a wszystkie migracje są przetwarzane w momencie uruchomienia aplikacji. Jednak z biegiem czasu możesz natknąć się na następujące przypadki:

  • Migracje stają się ogromne i zajmują dużo czasu.
  • Istnieje potrzeba migracji w środowiskach rozproszonych, na przykład na kilku instancjach serwerów baz danych jednocześnie.
    W takim przypadku zbyt długie stosowanie migracji spowoduje przekroczenie limitu czasu podczas uruchamiania aplikacji. Ponadto zastosowanie migracji do każdej instancji aplikacji z osobna może spowodować brak synchronizacji różnych serwerów.

Jak zdecydować

W takich przypadkach Twój projekt jest już duży, być może nawet dorosły, a Liquibase zaczyna działać jako osobne narzędzie zewnętrzne. Faktem jest, że Liquibase jako biblioteka jest skompilowana do pliku jar i może działać jako zależność w ramach projektu lub niezależnie.

W trybie autonomicznym możesz pozostawić realizację migracji swojemu środowisku CI/CD lub silnym barkom administratorów systemu i specjalistów ds. wdrażania. Aby to zrobić, będziesz potrzebować wiersza poleceń Liquibase https://www.liquibase.org/documentation/command_line.html. W tym trybie możliwe staje się uruchomienie aplikacji po przeprowadzeniu wszystkich niezbędnych migracji.

Wniosek

W rzeczywistości pułapek podczas migracji baz danych może być znacznie więcej, a wiele z nich wymaga kreatywnego podejścia. Ważne jest, aby zrozumieć, że jeśli użyjesz tego narzędzia prawidłowo, większości tych pułapek można uniknąć. Konkretnie musiałem uporać się ze wszystkimi wymienionymi problemami w różnej formie, a część z nich była wynikiem moich błędów. Najczęściej dzieje się tak oczywiście z powodu nieuwagi, ale czasami z powodu przestępczej nieumiejętności korzystania z narzędzia.

Źródło: www.habr.com

Dodaj komentarz