Wie man sich mit Liquibase nicht ins Bein schießt

Es war noch nie da, und jetzt ist es wieder soweit!

In unserem neuesten Projekt haben wir beschlossen, Liquibase von Anfang an zu verwenden, um zukünftige Probleme zu vermeiden. Dabei stellte sich heraus, dass nicht alle neuen Teammitglieder wissen, wie man es richtig anwendet. Ich habe einen internen Workshop durchgeführt, den ich später in einen Artikel umwandeln wollte.

Der Artikel enthält nützliche Tipps und beschreibt drei häufige Fallstricke, in die man beim Arbeiten mit Migrationstools für relationale Datenbanken, insbesondere Liquibase, geraten kann. Er richtet sich an Junior- und Mid-Level-Java-Entwickler. Für erfahrenere Entwickler kann er hilfreich sein, um Strukturen zu festigen und zu wiederholen, was ihnen wahrscheinlich bereits bekannt ist.

Wie man sich mit Liquibase nicht ins Bein schießt

Liquibase und Flyway sind die beiden Haupttechnologien, die rivalisierende Lösungen zur Versionskontrolle relationaler Strukturen in der Java-Welt bieten. Liquibase ist vollständig kostenlos und wird in der Praxis häufiger bevorzugt, weshalb es als Thema dieser Veröffentlichung gewählt wurde. Dennoch können einige der beschriebenen Praktiken universell anwendbar sein, abhängig von der Architektur Ihrer Anwendung.

Die Migration relationaler Strukturen ist eine notwendige Antwort auf die unzureichende Flexibilität relationaler Datenbanken. In einer Zeit, in der objektorientierte Programmierung in Mode war, ging man davon aus, dass wir einmal ein Schema entwerfen und es dann nie wieder ändern würden. Doch die Realität ist, dass sich alles ständig verändert, und Anpassungen an der Tabellenstruktur häufig erforderlich sind. Natürlich ist dieser Prozess oft schmerzhaft und unangenehm.

Ich werde nicht zu sehr ins Detail gehen, was die Technologie und die Anleitungen zur Integration der Bibliothek in Ihr Projekt betrifft; zu diesem Thema wurden bereits zahlreiche Artikel geschrieben:

Außerdem gab es bereits einen hervorragenden Artikel mit nützlichen Ratschlägen:

Tipps

Ich möchte meine Tipps und Kommentare teilen, die aus harter Arbeit, Schweiß und der Lösung von Migrationsproblemen entstanden sind.

1. Vor der Arbeit sollten Sie sich mit dem Abschnitt über die besten Praktiken auf Webseite Liquibase

vertraut machen. Dort werden einfache, aber sehr wichtige Aspekte beschrieben, ohne die die Nutzung der Bibliothek Ihnen das Leben erschweren könnte. Beispielsweise führt ein unstrukturierter Ansatz zur Verwaltung von Changesets früher oder später zu Verwirrung und fehlgeschlagenen Migrationen. Wenn voneinander abhängige Änderungen an der Datenbankstruktur und der Logik der Services nicht gleichzeitig ausgerollt werden, besteht eine hohe Wahrscheinlichkeit, dass dies zu fehlerhaften Tests oder einer beschädigten Umgebung führt. Darüber hinaus enthält die Empfehlung zur Nutzung von Liquibase auf der offiziellen Website einen Punkt zur Entwicklung und Überprüfung von Rollback-Skripten zusammen mit den Hauptmigration-Skripten. Außerdem gibt es im Artikel https://habr.com/ru/post/178665/ Codebeispiele zu Migrationen und dem Rollback-Mechanismus.

2. Wenn Sie mit Migrationstools arbeiten, vermeiden Sie manuelle Änderungen an der Datenbankstruktur.

Wie man so schön sagt: "Einmal Persil, immer Persil." Wenn die Datenbank Ihrer Anwendung mit Liquibase verwaltet wird, führt jede manuelle Änderung sofort zu einem inkonsistenten Zustand, und das Vertrauen in die Changesets sinkt auf null. Mögliche Risiken sind mehrere Stunden Aufwand für die Wiederherstellung der Datenbank, im schlimmsten Fall ein gestörter Server. Wenn jemand in Ihrem Team ein DBA-Architekt „alter Schule“ ist, erklären Sie ihm bitte geduldig und gründlich, wie schlecht es enden kann, wenn er die Datenbank nach eigenem Ermessen in einem beliebigen SQL Developer bearbeitet.

3. Wenn das Changeset bereits in das Repository gepusht wurde, vermeiden Sie eine Bearbeitung.

Wenn ein anderer Entwickler einen Pull-Request gemacht und einen Change-Set angewendet hat, der später bearbeitet wird, wird er sich freundlich an Sie erinnern, wenn ein Fehler beim Start der Anwendung auftritt. Wenn die Änderung in das Development-System gelangt, wird man einen mühsamen Weg über Hotfixes gehen müssen. Das Hauptproblem hängt mit der Validierung der Änderungen gemäß dem Hash-Wert zusammen – dem zentralen Mechanismus von Liquibase. Bei der Bearbeitung des Change-Sets ändert sich der Hash-Wert. Die Bearbeitung von Change-Sets ist nur dann möglich, wenn die gesamte Datenbank ohne Datenverlust neu aufgesetzt werden kann. In diesem Fall kann das Refactoring von SQL- oder XML-Code das Leben erleichtern und Migrationen leserlicher gestalten. Ein Beispiel wäre eine Situation, in der das Schema der ursprünglichen Datenbank beim Start der Anwendung im Team abgestimmt wurde.

4. Halte, wenn möglich, bewährte Backups der Datenbanken bereit.

Hier sollte alles klar sein. Falls die Migration unerfolgreich verläuft, kann alles zurückgegeben werden. In Liquibase gibt es ein Tool für den Rollback von Änderungen, jedoch erstellt der Entwickler auch die Rollback-Skripte, und diese können ebenso fehleranfällig sein wie die Skripte des Haupt-Change-Sets. Das bedeutet, dass es in jedem Fall sinnvoll ist, Backups anzulegen.

5. Nutze bewährte Datenbank-Backups in der Entwicklung, wenn möglich.

Wenn dies nicht gegen Verträge und Datenschutz verstößt, keine personenbezogenen Daten in der Datenbank vorhanden sind und sie nicht so schwer wie zwei Sonnen ist – kann vor dem Einsatz der Migration auf Live-Servern getestet werden, wie es auf der Maschine des Entwicklers funktioniert, wodurch nahezu 100 % der potenziellen Probleme bei der Migration vorhergesagt werden können.

6. Kommuniziere mit anderen Entwicklern im Team.

In einem gut organisierten Entwicklungsprozess weiß jeder im Team, woran er arbeitet. In der Realität ist das oft nicht der Fall. Wenn du also Änderungen an der Datenbankstruktur im Rahmen deiner Aufgabe vornimmst, ist es ratsam, das gesamte Team darüber zu informieren. Wenn jemand gleichzeitig Änderungen vornimmt, solltest du sorgfältig agieren. Es ist wichtig, auch nach Abschluss der Arbeit mit den Kollegen zu kommunizieren, nicht nur zu Beginn. Viele potenzielle Probleme mit Change-Sets können bereits während des Code-Reviews gelöst werden.

7. Denk nach, was du tust!

Es scheint ein offensichtlicher Ratschlag zu sein, der auf jede Situation anwendbar ist. Viele Probleme könnten jedoch vermieden werden, wenn der Entwickler noch einmal analysiert, was er tut und welche Auswirkungen das haben könnte. Der Umgang mit Migrationen erfordert immer zusätzliche Aufmerksamkeit und Sorgfalt.

Fallen

Lass uns nun typische Fallstricke betrachten, in die man geraten kann, wenn man die obigen Ratschläge ignoriert, und was man eigentlich tun sollte.

Situation 1. Zwei Entwickler versuchen gleichzeitig, neue Change-Sets hinzuzufügen.

Wie man sich mit Liquibase nicht ins Bein schießt
Wanja und Petja möchten ein Change Set für Version 4 erstellen, ohne voneinander zu wissen. Sie haben Änderungen an der Datenbankstruktur vorgenommen und einen Pull Request mit unterschiedlichen Dateien des Change Sets eingereicht. Im Folgenden wird ein Handlungsvorschlag unterbreitet:

Wie man es löst

  1. Irgendwie müssen die Kollegen sich darauf verständigen, in welcher Reihenfolge ihre Change Sets applied werden, wobei Petjas zuerst angewendet werden sollte.
  2. Jemand muss das zweite Change Set zu sich übernehmen und das Change Set von Wanja als Version 5 kennzeichnen. Dies kann durch Cherry Pick oder einen sorgfältigen Merge erfolgen.
  3. Nach den Änderungen sollte unbedingt die Gültigkeit der durchgeführten Aktionen überprüft werden.
    Tatsächlich ermöglichen die Liquibase-Mechanismen, zwei Change Sets für Version 4 im Repository zu haben, sodass alles so bleiben kann, wie es ist. Es wird einfach zwei Änderungen für Version 4 mit unterschiedlichen Namen geben. Bei diesem Ansatz wird es später in den Datenbankversionen sehr schwierig, den Überblick zu behalten.

Außerdem birgt Liquibase, ähnlich wie das Auenland, viele Geheimnisse. Eines davon ist der Schlüssel validCheckSum, der seit Version 1.7 existiert und es ermöglicht, einen gültigen Hashwert für einen bestimmten ChangeSet anzugeben, unabhängig davon, was in der Datenbank gespeichert ist. Die Dokumentation https://www.liquibase.org/documentation/changeset.html besagt Folgendes:

Fügen Sie einen Checksum-Wert hinzu, der für diesen ChangeSet als gültig betrachtet wird, unabhängig davon, was in der Datenbank gespeichert ist. Wird hauptsächlich verwendet, wenn Sie einen ChangeSet ändern müssen und nicht möchten, dass Fehler in Datenbanken auftreten, auf denen er bereits ausgeführt wurde (nicht empfohlene Vorgehensweise).

Ja, ja, eine solche Vorgehensweise wird nicht empfohlen. Aber manchmal beherrscht ein mächtiger Lichtmagier auch dunkle Techniken.

Situation 2. Migration, die von Daten abhängig ist.

Wie man sich mit Liquibase nicht ins Bein schießt

Angenommen, Sie haben keine Möglichkeit, Backups von Datenbanken auf Live-Servern zu verwenden. Petja hat einen ChangeSet erstellt, ihn lokal getestet und mit voller Überzeugung seiner Richtigkeit einen Pull Request in die Entwicklung gemacht. Der Projektleiter hat vorsichtshalber nachgefragt, ob Petja ihn überprüft hat, und hat ihn dann zusammengeführt. Aber das Deployment auf dem Entwicklungsserver ist fehlgeschlagen.

Es ist tatsächlich möglich, und niemand ist davor geschützt. Dies tritt ein, wenn Änderungen an der Tabellenstruktur in irgendeiner Weise an spezifische Daten aus der Datenbank gebunden sind. Offensichtlich kann die Datenbank von Peter, die nur mit Testdaten gefüllt ist, nicht alle problematischen Fälle abdecken. Zum Beispiel stellt man beim Löschen einer Tabelle fest, dass es Einträge in anderen Tabellen gibt, die über Foreign Keys mit den Einträgen in der zu löschenden Tabelle verbunden sind. Oder beim Ändern des Datentyps einer Spalte zeigt sich, dass nicht 100 % der Daten in den neuen Typ umgewandelt werden können.

Wie man es löst

  • Spezielle Skripte schreiben, die einmalig zusammen mit der Migration angewendet werden und die Daten in den richtigen Zustand versetzen. Dies ist ein allgemeiner Lösungsweg für das Problem des Datenimports in neue Strukturen, nachdem Migrationen angewendet wurden, aber etwas Ähnliches kann in bestimmten Fällen auch davor angewendet werden. Dieser Ansatz ist natürlich nicht immer möglich, da das Ändern von Daten auf aktiven Servern gefährlich und sogar katastrophal sein kann.
  • Ein weiterer komplexer Ansatz besteht darin, einen bestehenden Change-Satz zu bearbeiten. Die Schwierigkeit liegt darin, dass alle Datenbanken, in denen er in seiner aktuellen Form bereits angewendet wurde, wiederhergestellt werden müssen. Es ist gut möglich, dass das gesamte Backend-Team die Datenbanken lokal von Grund auf neu aufsetzen muss.
  • Der universellste Weg besteht darin, das Datenproblem auf die Umgebung des Entwicklers zu verlagern, die gleiche Situation nachzubilden und einen neuen Change-Satz hinzuzufügen, der vor dem fehlerhaften liegt und das Problem umgeht.
    Wie man sich mit Liquibase nicht ins Bein schießt

Im Allgemeinen gilt: Je mehr die Datenbasis in ihrer Zusammensetzung der Produktionsdatenbank ähnelt, desto geringer ist die Wahrscheinlichkeit, dass Probleme bei Migrationen eintreten. Und natürlich sollte man, bevor man den Change-Satz ins Repository sendet, mehrmals überlegen, ob er etwas kaputtmachen könnte.

Situation 3. Liquibase wird angewendet, nachdem es bereits in die Produktion gegangen ist.

Nehmen wir an, der Teamleiter hat Petja gebeten, Liquibase im Projekt zu integrieren, jedoch ist das Projekt bereits im Produktionsstatus und es gibt bereits eine bestehende Datenbankstruktur.

Das Hauptproblem besteht darin, dass auf allen neuen Servern oder Entwicklermaschinen die Daten dieser Tabellen von Grund auf neu erstellt werden müssen, während die bereits bestehende Umgebung in einem konsistenten Zustand verbleiben sollte, bereit für neue Changesets.

Wie man es löst

Hier gibt es mehrere Ansätze:

  • Der erste und offensichtlichste Ansatz ist, ein separates Skript zu haben, das manuell beim Initialisieren einer neuen Umgebung angewendet werden muss.
  • Der zweite — weniger offensichtliche — Ansatz ist, eine Liquibase-Migration zu haben, die sich in einem anderen Liquibase-Kontext befindet, und diese anzuwenden. Mehr über den Liquibase-Kontext kann man hier lesen: https://www.liquibase.org/documentation/contexts.html. Insgesamt ist dies ein interessantes Mechanismus, der erfolgreich angewendet werden kann, zum Beispiel für Testzwecke.
  • Der dritte Weg besteht aus mehreren Schritten. Zunächst muss eine Migration für die bereits vorhandenen Tabellen erstellt werden. Diese sollte dann in einer bestimmten Umgebung angewendet werden, um ihren Hash-Wert zu erhalten. Im nächsten Schritt sollten wir auf unserem nicht leeren Server leere Liquibase-Tabellen initialisieren und in die Historientabelle für die Anwendung der Changesets manuell einen Datensatz über ein "angeblich angewendetes" Changeset mit den bereits vorhandenen Änderungen in der Datenbank einfügen. So wird die Historie auf dem bereits bestehenden Server mit Version 2 beginnen, während alle neuen Umgebungen identisch verhalten werden.
    Wie man sich mit Liquibase nicht ins Bein schießt

Situation 4. Die Migrationen werden riesig und schaffen es nicht rechtzeitig, ausgeführt zu werden.

Zu Beginn der Entwicklung des Dienstes wird Liquibase in der Regel als externe Abhängigkeit eingesetzt, und alle Migrationen werden beim Start der Anwendung verarbeitet. Im Laufe der Zeit können jedoch folgende Szenarien auftreten:

  • Migrationen werden sehr groß und benötigen viel Zeit zur Ausführung.
  • Es entsteht die Notwendigkeit, Migrationen in verteilten Umgebungen durchzuführen, beispielsweise auf mehreren Instanzen von Datenbank-Servern gleichzeitig.
    In diesem Fall könnte eine zu lange Anwendung von Migrationen zu einem Timeout beim Start der Anwendung führen. Zudem kann die Anwendung von Migrationen für jede Instanz der Anwendung separat dazu führen, dass verschiedene Server nicht synchron sind.

Wie man es löst

In solchen Fällen ist Ihr Projekt bereits groß, möglicherweise sogar volljährig, und Liquibase fungiert zunehmend als separates externes Werkzeug. Liquibase wird nämlich als Bibliothek in eine JAR-Datei kompiliert und kann sowohl als Abhängigkeit innerhalb des Projekts als auch autonom arbeiten.

Im autonomen Modus können Sie die Anwendung von Migrationen Ihrer CI/CD-Umgebung oder den anhaltenden Schultern Ihrer Systemadministratoren überlassen. Dafür wird die Liquibase-Befehlszeile benötigt. https://www.liquibase.org/documentation/command_line.htmlIn diesem Modus ist es möglich, die Anwendung erst zu starten, nachdem alle erforderlichen Migrationen durchgeführt wurden.

Fazit

Tatsächlich gibt es bei der Arbeit mit Datenbankmigrationen viele Fallstricke, und viele davon erfordern einen kreativen Ansatz. Es ist wichtig zu verstehen, dass die meisten dieser Fallstricke vermieden werden können, wenn das Werkzeug richtig eingesetzt wird. Ich habe in verschiedenen Situationen mit all diesen Problemen zu tun gehabt, einige davon waren auch das Ergebnis meiner eigenen Fehler. In der Regel passiert das aus Unachtsamkeit, manchmal jedoch auch aus mangelndem Können im Umgang mit dem Werkzeug.

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster