Bereitstellung und Datenbanken ohne Ausfallzeiten

Bild


In diesem Artikel wird ausfĂŒhrlich erlĂ€utert, wie die mit der DatenbankkompatibilitĂ€t verbundenen Probleme wĂ€hrend der Bereitstellung behoben werden. Wir werden Ihnen mitteilen, was mit Ihren Anwendungen auf dem Produkt geschehen kann, wenn Sie versuchen, eine Bereitstellung ohne vorherige Vorbereitung durchzufĂŒhren. Dann werden wir die Phasen des Anwendungslebenszyklus durchlaufen, die notwendig sind, um keine Ausfallzeiten zu haben ( ca. Übersetzung: weiter - keine Ausfallzeiten ). Das Ergebnis unserer Operationen wird die Anwendung einer abwĂ€rtskompatiblen DatenbankĂ€nderung auf abwĂ€rtskompatible Weise sein.


Wenn Sie die Codebeispiele aus dem Artikel verstehen möchten, finden Sie sie auf GitHub .


Einleitung


Bereitstellung ohne Ausfallzeiten


Was ist die mystische Null-Ausfallzeit-Bereitstellung ? Dies können wir sagen, wenn Ihre Anwendung bereitgestellt wird, damit Sie eine neue Version der Anwendung erfolgreich fĂŒr die Produktion einfĂŒhren können, wĂ€hrend der Benutzer die UnzugĂ€nglichkeit nicht bemerkt. Dies ist aus Sicht des Anwenders und des Unternehmens das bestmögliche Deployment-Szenario, da auf diese Weise neue Funktionen eingefĂŒhrt und Fehler ohne Unterbrechung behoben werden können.


Wie kann man das erreichen? Es gibt verschiedene Möglichkeiten, hier eine davon:


  • Erweitern Sie Version 1 Ihres Dienstes
  • Migrieren Sie die Datenbank
  • Stellen Sie Version 2 Ihres Dienstes parallel zu Version 1 bereit
  • Sobald Sie feststellen, dass Version 2 ordnungsgemĂ€ĂŸ funktioniert, entfernen Sie Version 1
  • fertig!

Einfach, richtig? Leider ist dies nicht so einfach, und wir werden dies spĂ€ter im Detail betrachten. Schauen wir uns nun einen weiteren, recht hĂ€ufigen Bereitstellungsprozess an - die blaugrĂŒne Bereitstellung.


Haben Sie jemals von einem blau-grĂŒnen Einsatz gehört ? Mit Cloud Foundry ist dies extrem einfach. Schauen Sie sich diesen Artikel an, in dem wir ihn genauer beschreiben. Zusammenfassend erinnern wir uns kurz daran, wie die Bereitstellung von BlaugrĂŒn durchgefĂŒhrt wird:


  • Stellen Sie sicher, dass zwei Kopien Ihres Produktionscodes ("blau" und "grĂŒn") funktionieren.
  • den gesamten Verkehr in die blaue Umgebung leiten, d. h. damit die Produktions-URLs dorthin zeigen;
  • Stellen Sie alle AnwendungsĂ€nderungen in einer grĂŒnen Umgebung bereit und testen Sie sie
  • schalte die URL von blau auf grĂŒn um

Blue Green Deployment ist ein Ansatz, mit dem Sie auf einfache Weise neue Funktionen einfĂŒhren können, ohne befĂŒrchten zu mĂŒssen, dass die Produktion unterbrochen wird. Dies liegt an der Tatsache, dass Sie auch dann problemlos zur vorherigen Umgebung zurĂŒckkehren können, wenn Sie einfach auf einen Schalter klicken.


Nachdem Sie alle oben genannten Punkte gelesen haben, können Sie die Frage stellen: Was hat die Null-Ausfallzeit mit der blau-grĂŒnen Bereitstellung zu tun?


Nun, sie haben viele Gemeinsamkeiten, da fĂŒr die UnterstĂŒtzung von zwei Kopien derselben Umgebung doppelte Anstrengungen erforderlich sind. Dies ist der Grund, warum einige Teams laut Martin Fowler Variationen dieses Ansatzes verfolgen:


Eine andere Möglichkeit besteht darin, dieselbe Datenbank zu verwenden und blaugrĂŒne Switches fĂŒr Web- und Domain-Layer zu erstellen. Bei diesem Ansatz können Datenbanken hĂ€ufig ein Problem darstellen, insbesondere wenn Sie das Schema Ă€ndern mĂŒssen, um eine neue Version der Software zu unterstĂŒtzen.


Und hier kommen wir zum Hauptproblem in diesem Artikel. Die Datenbank . Schauen wir uns diesen Satz noch einmal an.


Migrieren Sie die Datenbank.


Nun mĂŒssen Sie sich die Frage stellen, was passiert, wenn das Ändern der Datenbank inkompatibel ist? Bricht meine erste Version der Anwendung nicht? Genau das wird passieren ...


Trotz der enormen Vorteile einer Bereitstellung ohne Ausfallzeiten und ohne "Blue Green" neigen Unternehmen dazu, den folgenden sichereren Bereitstellungsprozess fĂŒr ihre Anwendungen zu befolgen:


  • Bereiten Sie ein Paket mit einer neuen Version der Anwendung vor
  • Beenden Sie eine laufende Anwendung
  • FĂŒhren Sie Skripte fĂŒr die Datenbankmigration aus
  • Stellen Sie die neue Version der Anwendung bereit und starten Sie sie

In diesem Artikel wird ausfĂŒhrlich beschrieben, wie Sie mit einer Datenbank und Code arbeiten können, um die Bereitstellung ohne Ausfallzeiten zu nutzen.


Datenbankprobleme


Wenn Sie eine zustandslose Anwendung haben, in der keine Daten in der Datenbank gespeichert sind, können Sie die Bereitstellung ohne Ausfallzeiten sofort durchfĂŒhren. Leider muss die meiste Software Daten irgendwo speichern. Aus diesem Grund sollten Sie ĂŒberlegen, bevor Sie Änderungen an der Schaltung vornehmen. Bevor wir uns mit den Details zum Ändern des Schemas befassen, damit es ohne Ausfallzeiten bereitgestellt werden kann, konzentrieren wir uns zunĂ€chst auf das Versionskontrollschema.


Versionskontrollschema


In diesem Artikel verwenden wir Flyway als Tool fĂŒr die Versionskontrolle ( ungefĂ€hr ĂŒbersetzt: es handelt sich um eine Datenbankmigration ). NatĂŒrlich werden wir auch eine Spring Boot-Anwendung mit integrierter Flyway-UnterstĂŒtzung schreiben und die Schaltung migrieren, wĂ€hrend wir den Anwendungskontext einrichten. Bei Verwendung von Flyway können Sie Migrationsskripte in Ihrem Projektordner speichern (standardmĂ€ĂŸig im classpath:db/migration ). Hier sehen Sie ein Beispiel fĂŒr solche Migrationsdateien.


 └── db └── migration ├── V1__init.sql ├── V2__Add_surname.sql ├── V3__Final_migration.sql └── V4__Remove_lastname.sql 

In diesem Beispiel sehen wir 4 Migrationsszenarien, die nach dem Start der Anwendung nacheinander ausgefĂŒhrt werden, wenn sie nicht frĂŒher ausgefĂŒhrt wurden. Schauen wir uns als Beispiel eine der Dateien ( V1__init.sql ) an.


 CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer'); 

Alles spricht fĂŒr sich selbst: Mit SQL können Sie festlegen, wie Ihre Datenbank geĂ€ndert werden soll. Weitere Informationen zu Spring Boot und Flyway finden Sie unter Spring Boot-Dokumente .


Die Verwendung der Versionskontrolle mit Spring Boot bietet Ihnen zwei große Vorteile:


  • Sie trennen DatenbankĂ€nderungen von CodeĂ€nderungen
  • Die Datenbankmigration erfolgt zusammen mit dem Rollout Ihrer Anwendung, d. h. Ihr Bereitstellungsprozess wird vereinfacht

Lösen von Datenbankproblemen


Im nĂ€chsten Abschnitt des Artikels werden wir uns auf zwei AnsĂ€tze fĂŒr DatenbankĂ€nderungen konzentrieren.


  • RĂŒckwĂ€rts-InkompatibilitĂ€t
  • AbwĂ€rtskompatibilitĂ€t

Die erste Warnung wird als Warnung betrachtet, dass Sie ohne vorherige Vorbereitung keine Bereitstellung ohne Ausfallzeit durchfĂŒhren sollten. Die zweite bietet eine Lösung dafĂŒr, wie Sie eine Bereitstellung ohne Ausfallzeit durchfĂŒhren und gleichzeitig die AbwĂ€rtskompatibilitĂ€t aufrechterhalten können.


Unser Projekt, an dem wir arbeiten werden, wird eine einfache Spring Boot Flyway-Anwendung sein, in der sich eine Person mit first_name und last_name in der Datenbank befindet ( ungefÀhr pro: Person ist eine Tabelle, und irst_name und last_name sind die Felder darin ). Wir wollen last_name in surname umbenennen.


Annahmen


Bevor wir uns mit den Details befassen, mĂŒssen wir einige Annahmen zu unseren Anwendungen skizzieren. Das Hauptergebnis, das wir erreichen wollen, wird ein ziemlich einfacher Prozess sein.


Hinweis GeschÀft PRO-TIP. Durch die Rationalisierung von Prozessen können Sie beim Support viel Geld sparen (je mehr Mitarbeiter in Ihrem Unternehmen arbeiten, desto mehr Geld können Sie sparen)!

Die Datenbank muss nicht zurĂŒckgesetzt werden


Dies vereinfacht den Bereitstellungsprozess (einige Rollbacks der Datenbank sind fast unmöglich, z. B. das Löschen von Rollbacks). Wir ziehen es vor, nur Anwendungen zurĂŒckzusetzen. Auf diese Weise sieht Ihre Bereitstellungspipeline auch dann gleich aus, wenn Sie ĂŒber unterschiedliche Datenbanken verfĂŒgen (z. B. SQL und NoSQL).


Es muss IMMER möglich sein, die Anwendung um eine Version zurĂŒckzusetzen (nicht mehr)


Ein Rollback sollte nur bei Bedarf durchgefĂŒhrt werden. Wenn in der aktuellen Version ein Fehler auftritt, der nicht einfach zu beheben ist, sollten wir in der Lage sein, die neueste Arbeitsversion zurĂŒckzugeben. Wir gehen davon aus, dass es sich bei dieser neuesten Arbeitsversion um die vorherige handelt. Die Aufrechterhaltung der Code- und DatenbankkompatibilitĂ€t fĂŒr mehr als ein Rollout wĂ€re Ă€ußerst schwierig und kostspielig.


Hinweis Zur besseren Lesbarkeit werden wir im Rahmen dieses Artikels die Hauptversion der Anwendung Àndern.

Schritt 1: Ausgangszustand


Anwendungsversion: 1.0.0
DB Version: v1


Kommentar


Dies ist der Ausgangszustand der Anwendung.


DB Àndert sich


Die Datenbank enthÀlt den last_name.


 CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer'); 

Code-Änderungen


Die Anwendung speichert Personendaten unter last_name :


 /* * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.flyway; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Person { @Id @GeneratedValue private Long id; private String firstName; private String lastName; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return this.lastName; } public void setLastName(String lastname) { this.lastName = lastname; } @Override public String toString() { return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName + "]"; } } 

Inkompatible Spaltenumbenennung


Schauen wir uns ein Beispiel zum Ändern eines Spaltennamens an:


Achtung Das folgende Beispiel wird absichtlich unterbrochen. Wir zeigen dies, um das DatenbankkompatibilitÀtsproblem zu demonstrieren.

Anwendungsversion: 2.0.0.BAD


DB Version: v2bad


Kommentar


Aktuelle Änderungen ermöglichen es uns NICHT, zwei Instanzen (alte und neue) gleichzeitig auszufĂŒhren. Eine Bereitstellung ohne Ausfallzeiten ist daher schwierig zu erreichen (Annahmen, die berĂŒcksichtigt werden, dies ist praktisch unmöglich).


A / B-Test


Die aktuelle Situation ist, dass wir eine in prod bereitgestellte Anwendungsversion 1.0.0, und eine Datenbank v1 . Wir mĂŒssen die zweite Instanz der Anwendung, Version 2.0.0.BAD , 2.0.0.BAD und die Datenbank auf v2bad .


Schritte:


  1. stellte eine neue Instanz der Anwendungsversion 2.0.0.BAD , die die Datenbank auf v2bad
  2. Die Spalte " surname in der v2bad Datenbank nicht mehr vorhanden. Sie wurde in " surname geÀndert
  3. Datenbank- und Anwendungsaktualisierungen waren erfolgreich, und einige Instanzen funktionieren in 1.0.0 , andere in 2.0.0.BAD . Alle verwandt mit v2bad
  4. Alle Instanzen von Version 1.0.0 last_name Fehler aus, da sie versuchen, Daten in die Spalte last_name einzufĂŒgen, die nicht mehr vorhanden ist
  5. Alle Instanzen von Version 2.0.0.BAD funktionieren ohne Probleme

Wie Sie sehen, sind A / B-Tests nicht möglich, wenn wir inkompatible Änderungen an der Datenbank und den Anwendungen vornehmen.


Rollback-Anwendung


Nehmen wir an, dass wir nach dem Versuch, eine A / B-Bereitstellung durchzufĂŒhren ( ungefĂ€hr ĂŒbersetzt: wahrscheinlich bezog sich der Autor hier auf A / B-Tests ), entschieden haben, dass wir die Anwendung auf Version 1.0.0. Angenommen, wir möchten die Datenbank nicht zurĂŒcksetzen.


Schritte:


  1. Wir stoppen die Anwendungsinstanz Version 2.0.0.BAD
  2. Datenbank ist noch v2bad
  3. Da Version 1.0.0 surname nicht versteht, werden Fehler angezeigt
  4. Die Hölle brach los, wir können nicht mehr zurĂŒckkehren

Wie Sie sehen, können wir bei inkompatiblen Änderungen an der Datenbank und den Anwendungen kein Rollback auf die vorherige Version durchfĂŒhren.


SkriptausfĂŒhrungsprotokolle


 Backward incompatible scenario: 01) Run 1.0.0 02) Wait for the app (1.0.0) to boot 03) Generate a person by calling POST localhost:9991/person to version 1.0.0 04) Run 2.0.0.BAD 05) Wait for the app (2.0.0.BAD) to boot 06) Generate a person by calling POST localhost:9991/person to version 1.0.0 <-- this should fail 07) Generate a person by calling POST localhost:9992/person to version 2.0.0.BAD <-- this should pass Starting app in version 1.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"} Starting app in version 2.0.0.BAD Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: curl: (22) The requested URL returned error: 500 Internal Server Error Generate a person in version 2.0.0.BAD Sending a post to 127.0.0.1:9995/person. This is the response: {"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"} 

DB Àndert sich


Migrationsskript, das last_name in surname last_name


Quell-Flyway-Skript:


 CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer'); 

Ein Skript, das den last_name .


 -- This change is backward incompatible - you can't do A/B testing ALTER TABLE PERSON CHANGE last_name surname VARCHAR; 

Code-Änderungen


Wir haben den lastName surname in surname geÀndert.


Eine Spalte abwÀrtskompatibel umbenennen


Dies ist die hĂ€ufigste Situation, der wir begegnen können. Wir mĂŒssen rĂŒckwĂ€rts inkompatible Änderungen vornehmen. Wir haben bereits bewiesen, dass fĂŒr eine Bereitstellung ohne Ausfallzeiten nicht nur die Datenbankmigration ohne zusĂ€tzliche Maßnahmen angewendet werden sollte. In diesem Abschnitt des Artikels werden 3 Bereitstellungen der Anwendung sowie Datenbankmigrationen durchgefĂŒhrt, um das gewĂŒnschte Ergebnis zu erzielen und gleichzeitig die AbwĂ€rtskompatibilitĂ€t zu gewĂ€hrleisten.


Hinweis Denken Sie daran, dass wir eine Datenbankversion v1 . Es enthĂ€lt die Spalten first_name und last_name . Wir mĂŒssen last_name in surname Ă€ndern. Wir haben auch eine Anwendungsversion 1.0.0, , die noch keinen surname .

Schritt 2: Nachname hinzufĂŒgen


Anwendungsversion: 2.0.0
DB Version: v2


Kommentar


Durch HinzufĂŒgen einer neuen Spalte und Kopieren ihres Inhalts erstellen wir abwĂ€rtskompatible DatenbankĂ€nderungen. Wenn wir die JAR-Datei zurĂŒcksetzen oder eine funktionierende alte JAR-Datei haben, wird sie zur Laufzeit nicht beschĂ€digt.


Wir rollen die neue Version aus


Schritte:


  1. Migrieren Sie die Datenbank, um eine neue surname zu erstellen. Jetzt deine db version v2
  2. Kopieren Sie die Daten von last_name zu surname . Beachten Sie, dass Sie eine Batch-Migration in Betracht ziehen sollten, wenn Sie ĂŒber viele dieser Daten verfĂŒgen!
  3. Schreiben Sie Code, in dem sowohl die neue als auch die alte Spalte verwendet werden. Nun Ihre Anwendungsversion 2.0.0
  4. Lesen Sie den Wert aus der Spalte surname , wenn er nicht null , oder aus ast_name wenn surname nicht angegeben ist. Sie können getLastName() aus dem Code entfernen, da es null zurĂŒckgibt, wenn Sie Ihre Anwendung von 3.0.0 auf 2.0.0 .

Wenn Sie Spring Boot Flyway verwenden, werden diese beiden Schritte beim Start von Version 2.0.0 Anwendung ausgefĂŒhrt. Wenn Sie das Datenbankversionierungstool manuell ausfĂŒhren, mĂŒssen Sie zwei verschiedene Aktionen ausfĂŒhren (zuerst die Datenbankversion manuell aktualisieren und dann eine neue Anwendung bereitstellen).


Es ist wichtig. Denken Sie daran, dass eine neu erstellte Spalte NICHT NICHT NULL sein darf. Wenn Sie ein Rollback durchfĂŒhren, kennt die alte Anwendung die neue Spalte nicht und installiert sie beim Insert. Wenn Sie diese EinschrĂ€nkung jedoch hinzufĂŒgen und Ihre Datenbank die v2 , muss der Wert der neuen Spalte festgelegt werden. Was zu VerstĂ¶ĂŸen gegen BeschrĂ€nkungen fĂŒhren wird.

Es ist wichtig. Sie sollten die Methode getLastName() entfernen, da Version 3.0.0 nicht das Konzept einer Spalte last_name im Code hat. Dies bedeutet, dass dort null gesetzt wird. Sie können die Methode verlassen und getSurname() hinzufĂŒgen. Eine viel bessere Lösung wĂ€re jedoch, sicherzustellen, dass Sie in der getSurname() den richtigen Wert ungleich Null ausgewĂ€hlt haben.

A / B-Test


Die aktuelle Situation besteht darin, dass auf dem Produkt eine Anwendungsversion 1.0.0 und in Version 1 eine Datenbank v1 . Wir mĂŒssen die zweite Instanz der Anwendungsversion 2.0.0 bereitstellen, mit der die Datenbank auf Version 2 aktualisiert wird.


Schritte:


  1. stellte eine neue Instanz der Anwendungsversion 2.0.0 , mit der die Datenbank auf Version 2 aktualisiert wird
  2. In der Zwischenzeit wurden einige Anforderungen von Instanzen der Version 1.0.0
  3. Das Update war erfolgreich, und Sie haben mehrere Arbeitsinstanzen der Anwendungsversion 1.0.0 und der restlichen Version 2.0.0. Jeder kommuniziert mit der Datenbank in v2
  4. Version 1.0.0 verwendet nicht die Nachname-Spalte in der Datenbank, aber Version 2.0.0 verwendet. Sie stören sich nicht und es sollten keine Fehler auftreten.
  5. Version 2.0.0 speichert Daten sowohl in der alten als auch in der neuen Spalte, was AbwÀrtskompatibilitÀt bietet

Es ist wichtig. Wenn Sie Abfragen haben, die Elemente basierend auf Werten aus der alten / neuen Spalte zĂ€hlen, sollten Sie daran denken, dass Sie jetzt doppelte Werte haben (höchstwahrscheinlich werden sie noch migriert). Wenn Sie beispielsweise die Anzahl der Benutzer zĂ€hlen möchten, deren Nachname (unabhĂ€ngig vom Namen der Spalte) mit dem Buchstaben A beginnt, haben Sie möglicherweise inkonsistente Daten, bevor die Datenmigration ( old → new Spalte) abgeschlossen ist, wenn Sie eine Abfrage fĂŒr eine neue Spalte ausfĂŒhren.

Rollback-Anwendung


Jetzt haben wir eine Anwendungsversion 2.0.0 und eine Datenbank in v2 .


Schritte:


  1. Setzen Sie Ihre Anwendung auf Version 1.0.0 .
  2. In Version 1.0.0 wird die surname Spalte in der Datenbank nicht verwendet, daher muss das Rollback erfolgreich sein

DB Àndert sich


Die Datenbank enthÀlt eine Spalte mit dem Namen last_name .


Quell-Flyway-Skript:


 CREATE TABLE PERSON ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, first_name varchar(255) not null, last_name varchar(255) not null ); insert into PERSON (first_name, last_name) values ('Dave', 'Syer'); 

Das Skript zum HinzufĂŒgen des surname .


Achtung Denken Sie daran, dass Sie der hinzugefĂŒgten Spalte KEINE NOT NULL-EinschrĂ€nkungen HINZUFÜGEN KÖNNEN. Wenn Sie die JAR-Datei zurĂŒcksetzen, hat die alte Version keine Ahnung von der hinzugefĂŒgten Spalte und setzt sie automatisch auf NULL. Wenn es eine solche EinschrĂ€nkung gibt, wird die alte Anwendung einfach unterbrochen.

 -- NOTE: This field can't have the NOT NULL constraint cause if you rollback, the old version won't know about this field -- and will always set it to NULL ALTER TABLE PERSON ADD surname varchar(255); -- WE'RE ASSUMING THAT IT'S A FAST MIGRATION - OTHERWISE WE WOULD HAVE TO MIGRATE IN BATCHES UPDATE PERSON SET PERSON.surname = PERSON.last_name 

Code-Änderungen


Wir speichern Daten sowohl im last_name als auch im surname . Gleichzeitig lesen wir von last_name , da diese Spalte am relevantesten ist. WÀhrend des Bereitstellungsprozesses wurden einige Anforderungen möglicherweise von einer Anwendungsinstanz verarbeitet, die noch nicht aktualisiert wurde.


 /* * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.flyway; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Person { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String surname; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } /** * Reading from the new column if it's set. If not the from the old one. * * When migrating from version 1.0.0 -> 2.0.0 this can lead to a possibility that some data in * the surname column is not up to date (during the migration process lastName could have been updated). * In this case one can run yet another migration script after all applications have been deployed in the * new version to ensure that the surname field is updated. * * However it makes sense since when looking at the migration from 2.0.0 -> 3.0.0. In 3.0.0 we no longer * have a notion of lastName at all - so we don't update that column. If we rollback from 3.0.0 -> 2.0.0 if we * would be reading from lastName, then we would have very old data (since not a single datum was inserted * to lastName in version 3.0.0). */ public String getSurname() { return this.surname != null ? this.surname : this.lastName; } /** * Storing both FIRST_NAME and SURNAME entries */ public void setSurname(String surname) { this.lastName = surname; this.surname = surname; } @Override public String toString() { return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName + ", surname=" + this.surname + "]"; } } 

Schritt 3: Entfernen von Nachname aus dem Code


Anwendungsversion: 3.0.0


DB Version: v3


Kommentar


Hinweis trans .: Offensichtlich hat der Autor den Text dieses Blocks aus Schritt 2 des Originalartikels fĂ€lschlicherweise kopiert. In diesem Schritt sollten Änderungen am Anwendungscode vorgenommen werden, um die FunktionalitĂ€t zu entfernen, die die Spalte last_name .


Durch HinzufĂŒgen einer neuen Spalte und Kopieren ihres Inhalts haben wir abwĂ€rtskompatible DatenbankĂ€nderungen erstellt. Auch wenn wir das JAR zurĂŒcksetzen oder ein funktionierendes altes JAR haben, wird es zur Laufzeit nicht beschĂ€digt.


Rollback-Anwendung


Wir haben derzeit die Version 3.0.0 Anwendung und die v3 Datenbank. Version 3.0.0 speichert keine Daten in last_name . Dies bedeutet, dass der surname die aktuellsten Informationen speichert.


Schritte:


  1. FĂŒhren Sie ein Rollback Ihrer Anwendung auf Version 2.0.0 .
  2. Version 2.0.0 verwendet sowohl last_name als auch surname .
  3. Version 2.0.0 nimmt den surname wenn er nicht null ist, andernfalls last_name

DB Àndert sich


Es gibt keine strukturellen Änderungen in der Datenbank. Das folgende Skript wird ausgefĂŒhrt, das die endgĂŒltige Migration alter Daten durchfĂŒhrt:


 -- WE'RE ASSUMING THAT IT'S A FAST MIGRATION - OTHERWISE WE WOULD HAVE TO MIGRATE IN BATCHES -- ALSO WE'RE NOT CHECKING IF WE'RE NOT OVERRIDING EXISTING ENTRIES. WE WOULD HAVE TO COMPARE -- ENTRY VERSIONS TO ENSURE THAT IF THERE IS ALREADY AN ENTRY WITH A HIGHER VERSION NUMBER -- WE WILL NOT OVERRIDE IT. UPDATE PERSON SET PERSON.surname = PERSON.last_name; -- DROPPING THE NOT NULL CONSTRAINT; OTHERWISE YOU WILL TRY TO INSERT NULL VALUE OF THE LAST_NAME -- WITH A NOT_NULL CONSTRAINT. ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL; 

Code-Änderungen


Hinweis trans .: Die Beschreibung dieses Blocks wurde vom Autor auch fĂ€lschlicherweise aus Schritt 2 kopiert. GemĂ€ĂŸ der Logik der Artikelgeschichte sollten Änderungen im Code in diesem Schritt darauf abzielen, Elemente, die mit der Spalte last_name funktionieren, daraus zu last_name .


Wir speichern Daten sowohl im last_name als auch im surname. Außerdem lesen wir aus der Spalte last_name , da dies am relevantesten ist. WĂ€hrend des Bereitstellungsprozesses werden einige Anforderungen möglicherweise von einer Instanz verarbeitet, die noch nicht aktualisiert wurde.


 /* * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.flyway; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Person { @Id @GeneratedValue private Long id; private String firstName; private String surname; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getSurname() { return this.surname; } public void setSurname(String lastname) { this.surname = lastname; } @Override public String toString() { return "Person [firstName=" + this.firstName + ", surname=" + this.surname + "]"; } } 

Schritt 4: Entfernen von Nachname aus der Datenbank


Anwendungsversion: 4.0.0


DB Version: v4


Kommentar


Aufgrund der Tatsache, dass der Code der Version 3.0.0 die Spalte last_name nicht verwendet last_name , wird wĂ€hrend der AusfĂŒhrung nichts Schlimmes passieren, wenn nach dem Entfernen der Spalte aus der Datenbank ein Rollback auf 3.0.0 .


SkriptausfĂŒhrungsprotokolle


 We will do it in the following way: 01) Run 1.0.0 02) Wait for the app (1.0.0) to boot 03) Generate a person by calling POST localhost:9991/person to version 1.0.0 04) Run 2.0.0 05) Wait for the app (2.0.0) to boot 06) Generate a person by calling POST localhost:9991/person to version 1.0.0 07) Generate a person by calling POST localhost:9992/person to version 2.0.0 08) Kill app (1.0.0) 09) Run 3.0.0 10) Wait for the app (3.0.0) to boot 11) Generate a person by calling POST localhost:9992/person to version 2.0.0 12) Generate a person by calling POST localhost:9993/person to version 3.0.0 13) Kill app (3.0.0) 14) Run 4.0.0 15) Wait for the app (4.0.0) to boot 16) Generate a person by calling POST localhost:9993/person to version 3.0.0 17) Generate a person by calling POST localhost:9994/person to version 4.0.0 Starting app in version 1.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"} Starting app in version 2.0.0 Generate a person in version 1.0.0 Sending a post to 127.0.0.1:9991/person. This is the response: {"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"} Generate a person in version 2.0.0 Sending a post to 127.0.0.1:9992/person. This is the response: {"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"} Killing app 1.0.0 Starting app in version 3.0.0 Generate a person in version 2.0.0 Sending a post to 127.0.0.1:9992/person. This is the response: {"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"} Generate a person in version 3.0.0 Sending a post to 127.0.0.1:9993/person. This is the response: {"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"} Killing app 2.0.0 Starting app in version 4.0.0 Generate a person in version 3.0.0 Sending a post to 127.0.0.1:9993/person. This is the response: {"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"} Generate a person in version 4.0.0 Sending a post to 127.0.0.1:9994/person. This is the response: {"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"} 

DB Àndert sich


FĂŒr v3 entfernen wir einfach die last_name Spalte und fĂŒgen die fehlenden EinschrĂ€nkungen hinzu.


 -- REMOVE THE COLUMN ALTER TABLE PERSON DROP last_name; -- ADD CONSTRAINTS UPDATE PERSON SET surname='' WHERE surname IS NULL; ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL; 

Code-Änderungen


Es gibt keine Änderungen im Code.


Fazit


Wir haben die abwĂ€rtskompatible Änderung des Spaltennamens erfolgreich angewendet, indem wir mehrere abwĂ€rtskompatible Bereitstellungen durchgefĂŒhrt haben. Nachfolgend finden Sie eine Zusammenfassung der durchgefĂŒhrten Schritte:


  1. Application Deployment Version 1.0.0 mit v1 Datenbankschema (Spaltenname = last_name )
  2. last_name die Anwendungsversion 2.0.0, last_name 2.0.0, der Daten in last_name und surname last_name . Die Anwendung liest aus last_name . Die Datenbank ist in Version v2 , die die Spalten last_name und surname. surname surname. surname ist eine Kopie von l ast_name . (HINWEIS: Diese Spalte darf keine Nicht-Null-EinschrÀnkung enthalten.)
  3. Bereitstellung einer Anwendung der Version 3.0.0 , bei der Daten nur im surname gespeichert und vom Nachnamen gelesen werden. Wie bei der Datenbank erfolgt die letzte Migration von last_name zu surname . Außerdem wird die NOT NULL-EinschrĂ€nkung aus last_name . DB ist jetzt in der Version v3
  4. Application Deployment Version 4.0.0 - Es werden keine Änderungen am Code vorgenommen. Stellen Sie eine v4 Datenbank last_name , die den last_name entfernt. Hier können Sie fehlende EinschrĂ€nkungen zur Datenbank hinzufĂŒgen.

Nach diesem Ansatz können Sie immer eine Version zurĂŒcksetzen, ohne die Datenbank- / AnwendungskompatibilitĂ€t zu beeintrĂ€chtigen.


Code


Der gesamte in diesem Artikel verwendete Code ist auf Github verfĂŒgbar. Unten finden Sie eine zusĂ€tzliche Beschreibung.


Projekte


Nach dem Klonen des Repositorys wird die folgende Ordnerstruktur angezeigt.


 ├── boot-flyway-v1 - 1.0.0 version of the app with v1 of the schema ├── boot-flyway-v2 - 2.0.0 version of the app with v2 of the schema (backward-compatible - app can be rolled back) ├── boot-flyway-v2-bad - 2.0.0.BAD version of the app with v2bad of the schema (backward-incompatible - app cannot be rolled back) ├── boot-flyway-v3 - 3.0.0 version of the app with v3 of the schema (app can be rolled back) └── boot-flyway-v4 - 4.0.0 version of the app with v4 of the schema (app can be rolled back) 


, , .


, :


 ./scripts/scenario_backward_compatible.sh 

, :


 ./scripts/scenario_backward_incompatible.sh 

Spring Boot Sample Flyway


Spring Boot Sample Flyway.


http://localhost:8080/flyway , .


H2 ( http://localhost:8080/h2-console ), (URL jdbc — jdbc:h2:mem:testdb ).


Optional



Lesen Sie auch andere Artikel in unserem Blog:


Source: https://habr.com/ru/post/de481932/


All Articles