
W tym artykule szczegółowo wyjaśniamy, jak rozwiązywać problemy związane z kompatybilnością baz danych podczas wdrażania. Opowiemy, co może się stać z Twoimi aplikacjami na produkcji, jeśli spróbujesz wykonać wdrożenie bez wcześniejszego przygotowania. Następnie przejdziemy przez etapy cyklu życia aplikacji, które są niezbędne, aby osiągnąć zero downtime (przyp. red.: dalej — zero downtime). Wynikiem naszych operacji będzie zastosowanie cofających zmian bazy danych w sposób wspierający kompatybilność.
Jeśli chcesz zapoznać się z przykładami kodu z artykułu, znajdziesz je na .
Wprowadzenie
Zero downtime deployment
Czym jest tajemniczy zero downtime deployment? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.
Jak to osiągnąć? Istnieje kilka sposobów, oto jeden z nich:
- wdroż swoją wersję nr 1 usługi
- wykonaj migrację bazy danych
- wdroż wersję nr 2 swojej usługi równolegle z wersją nr 1
- gdy tylko zobaczysz, że wersja nr 2 działa tak, jak należy, usuń wersję nr 1
- gotowe!
Łatwe, prawda? Niestety, to nie jest takie proste i później omówimy to bardziej szczegółowo. Teraz sprawdźmy jeszcze jeden dość powszechny proces wdrażania — blue green deployment.
Czy kiedykolwiek słyszałeś o ? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на , w którym opisujemy to bardziej szczegółowo. Krótko podsumowując, przypomnijmy, jak przeprowadzać blue green deployment:
- zapewnij działanie dwóch kopii swojego kodu produkcyjnego („blue” i „green”);
- kieruj cały ruch do środowiska blue, tzn. aby adresy URL produkcji wskazywały tam;
- wdrażaj i testuj wszystkie zmiany aplikacji w środowisku green;
- przełącz adresy URL z blue na green
Blue green deployment to podejście, które umożliwia łatwe wprowadzanie nowych funkcji, nie martwiąc się o to, że produkcja się zepsuje. Wynika to z faktu, że nawet jeśli coś się wydarzy, możesz łatwo powrócić do poprzedniego środowiska, po prostu „przełączając przycisk”.
Po przeczytaniu powyższego możesz zadać pytanie: Jakie znaczenie zero downtime ma dla Blue green deploymentu?
Cóż, mają wiele wspólnego, ponieważ utrzymanie dwóch kopii tego samego środowiska wymaga podwójnego wysiłku. Dlatego niektóre zespoły, jak twierdzi , przyjmują wariant tego podejścia:
Innym sposobem jest użycie tej samej bazy danych, tworząc niebiesko-zielone przełączniki dla warstw web i domain. W takim podejściu bazy danych często mogą być problemem, szczególnie gdy trzeba zmienić jej schemat, aby wspierać nową wersję oprogramowania.
I tutaj dochodzimy do głównego problemu w tym artykule. Baza danych.. Przyjrzyjmy się jeszcze raz temu zdaniu.
przeprowadź migrację bazy danych.
Teraz musisz zadać sobie pytanie — co jeśli zmiana bazy danych jest wstecznie niezgodna? Czy moja pierwsza wersja aplikacji nie przestanie działać? W rzeczywistości, właśnie to się stanie…
W ten sposób, mimo ogromnych zalet zero downtime / blue green deployment, firmy tendencjonują do postępowania według bardziej bezpiecznego procesu wdrożenia swoich aplikacji:
- przygotować pakiet z nową wersją aplikacji
- wyłączyć uruchomioną aplikację
- uruchomić skrypty migracji bazy danych
- wdrożyć i uruchomić nową wersję aplikacji
W tym artykule szczegółowo omówimy, jak możesz pracować z bazą danych i kodem, aby skorzystać z zerowego czasu przestoju przy wdrożeniu.
Problemy z bazą danych
Jeśli masz aplikację stateless, która nie przechowuje żadnych danych w bazie danych, możesz uzyskać zero downtime deployment od razu. Niestety, większość oprogramowania musi gdzieś przechowywać dane. Dlatego należy dwa razy pomyśleć, zanim wprowadzi się jakiekolwiek zmiany w schemacie. Zanim zagłębimy się w szczegóły, jak zmienić schemat w taki sposób, aby możliwe było wdrożenie bez przestoju, skoncentrujmy się najpierw na schemacie zarządzania wersjami.
Schemat zarządzania wersjami
W tym artykule będziemy używać jako narzędzia do zarządzania wersjami (przyp. red.: chodzi o migracje bazy danych). Oczywiście, napiszemy również aplikację Spring Boot, która ma wbudowane wsparcie dla Flyway i wykona migrację schematu podczas konfigurowania kontekstu aplikacji. Używając Flyway, możesz przechowywać skrypty migracyjne w folderze swoich projektów (domyślnie w classpath:db/migration). Tutaj możesz zobaczyć przykład takich plików migracji
└── db
└── migration
├── V1__init.sql
├── V2__Add_surname.sql
├── V3__Final_migration.sql
└── V4__Remove_lastname.sqlW tym przykładzie widzimy 4 scenariusze migracji, które, jeśli nie zostały wcześniej zrealizowane, będą wykonywane jeden po drugim podczas uruchamiania aplikacji. Przyjrzyjmy się jednemu z plików (V1__init.sql) jako przykład.
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');Wszystko samo mówi za siebie: możesz używać SQL, aby określić, jak Twoja baza danych powinna być zmieniona. Aby uzyskać więcej informacji na temat Spring Boot i Flyway, zapoznaj się z .
Używając narzędzia do zarządzania wersjami z Spring Boot, zyskujesz 2 ogromne korzyści:
- oddzielasz zmiany w bazie danych od zmian w kodzie
- migracja bazy danych ma miejsce razem z wdrożeniem Twojej aplikacji, co upraszcza Twój proces wdrożeniowy
Rozwiązywanie problemów z bazą danych
W następnej części artykułu skupimy się na rozważeniu dwóch podejść do zmian w bazie danych.
- niezgodność wsteczna
- zgodność wsteczna
Pierwsze zostanie omówione jako ostrzeżenie, że nie należy przeprowadzać wdrożenia bez przestoju bez wcześniejszego przygotowania… Drugie proponuje rozwiązanie, jak przeprowadzić wdrożenie bez przestojów i jednocześnie wprowadzać zgodność wstecz.
Nasz projekt, nad którym będziemy pracować, będzie prostą aplikacją Spring Boot Flyway, która zawiera Person z first_name i last_name w bazie danych (przyp. tłum.: Person jest tabelą, a first_name i last_name to pola w niej). Chcemy zmienić nazwę last_name do surname.
Założenia
Zanim zagłębimy się w szczegóły, musimy określić kilka założeń dotyczących naszych aplikacji. Głównym wynikiem, który chcemy osiągnąć, będzie dość prosty proces.
Uwaga. Wskazówka biznesowa: Uproszczenie procesów może zaoszczędzić Ci dużo pieniędzy na utrzymaniu (im więcej osób pracuje w Twojej firmie, tym więcej pieniędzy możesz zaoszczędzić)!
Nie należy cofać bazy danych
Ułatwia to proces wdrożenia (niektóre cofnięcia bazy danych są praktycznie niemożliwe, na przykład cofnięcie usunięcia). Wolimy cofać tylko aplikacje. Dzięki temu, nawet jeśli masz różne bazy danych (na przykład SQL i NoSQL), Twój proces wdrożenia będzie wyglądał tak samo.
Musi zawsze być możliwość cofnięcia aplikacji o jedną wersję wstecz (nie więcej)
Rollback should only be performed when necessary. If there is an error in the current version that is difficult to fix, we must be able to revert to the last working version. We assume that this last working version is the previous one. Maintaining compatibility for code and the database across more than one rollout would be extremely difficult and costly.
NotatkaFor better readability, in this article we will change the major version of the application.
Step 1: Initial state
Application version: 1.0.0
Database version: v1
Komentarz
This will be the initial state of the application.
Database changes
The database contains 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 changes
The application saves Person data in 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
+ "]";
}
}Backwards incompatible column renaming
Let's look at an example of how to change a column name:
Attention. The following example will intentionally lead to a breakage. We show this to demonstrate the database compatibility issue.
Application version: 2.0.0.BAD
Database version: v2bad
Komentarz
The current changes do NOT allow us to run two instances (old and new) simultaneously. Thus, zero downtime deployment would be difficult to achieve (given the assumptions, it is practically impossible).
A/B testing
The current situation is that we have an application version 1.0.0, deployed in production, and the database v1. We need to deploy a second instance of the application, version 2.0.0.BAD, and update the database to v2bad.
Kroki:
- a new instance of application version
2.0.0.BAD, which updates the database tov2bad - w bazie danych
v2badthe columnlast_nameno longer exists — it has been changed tosurname - the database and application update was successful, and some instances are running in
1.0.0, others in2.0.0.BAD. All are connected to the databasev2bad - all instances of version
1.0.0will start throwing errors because they will try to insert data into the columnlast_name, which no longer exists - all instances of version
2.0.0.BADwill work without issues
As you can see, if we make backwards incompatible changes to the database and application, A/B testing is impossible.
Application rollback
Let's assume that after attempting to perform A/B deployment (note: the author probably meant A/B testing) we decided that we need to rollback the application to version 1.0.0. Let's say we do not want to rollback the database.
Kroki:
- zatrzymujemy instancję aplikacji wersji
2.0.0.BAD - baza danych nadal
v2bad - ponieważ wersja
1.0.0nie rozumie, co to jestsurname, zobaczymy błędy - piekło uwolniło się, nie możemy już wrócić
Jak widać, jeśli wprowadzamy niekompatybilne zmiany w bazie danych i aplikacji, nie możemy cofnąć się do poprzedniej wersji.
Logi wykonania skryptu
Scenariusz niekompatybilności wstecznej:
01) Uruchom 1.0.0
02) Poczekaj na uruchomienie aplikacji (1.0.0)
03) Wygeneruj osobę, wysyłając POST localhost:9991/person do wersji 1.0.0
04) Uruchom 2.0.0.BAD
05) Poczekaj na uruchomienie aplikacji (2.0.0.BAD)
06) Wygeneruj osobę, wysyłając POST localhost:9991/person do wersji 1.0.0 <-- to powinno się nie udać
07) Wygeneruj osobę, wysyłając POST localhost:9992/person do wersji 2.0.0.BAD <-- to powinno przejść
Uruchamianie aplikacji w wersji 1.0.0
Wygeneruj osobę w wersji 1.0.0
Wysyłanie posta do 127.0.0.1:9991/person. Oto odpowiedź:
{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}
Uruchamianie aplikacji w wersji 2.0.0.BAD
Wygeneruj osobę w wersji 1.0.0
Wysyłanie posta do 127.0.0.1:9991/person. Oto odpowiedź:
curl: (22) Żądany URL zwrócił błąd: 500 Internal Server Error
Wygeneruj osobę w wersji 2.0.0.BAD
Wysyłanie posta do 127.0.0.1:9995/person. Oto odpowiedź:
{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}Database changes
Skrypt migracji, który zmienia nazwy last_name do surname
Skrypt Flyway źródłowy:
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');Skrypt, który zmienia nazwy last_name.
-- Ta zmiana jest niekompatybilna wstecz - nie możesz testować A/B
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;Code changes
Zmieniliśmy nazwę pola lastName na surname.
Zmienianie nazwy kolumny w sposób kompatybilny z wstecz
To najbardziej typowa sytuacja, z którą możemy się spotkać. Musimy wprowadzać zmiany niekompatybilne wstecz. Już udowodniliśmy, że aby wdrożyć bez przestojów, nie powinniśmy po prostu stosować migracji bazy danych bez dodatkowych działań. W tej części artykułu przeprowadzimy 3 wdrożenia aplikacji wraz z migracjami bazy danych, aby osiągnąć pożądany wynik i jednocześnie zachować zgodność z wstecz.
Uwaga. Przypomnijmy, że mamy bazę danych wersji
v1. Zawiera kolumnyfirst_nameilast_name. Musimy zmienićlast_namenasurname. Mamy również aplikację wersji1.0.0,która jeszcze nie używasurname.
Krok 2: Dodajemy surname
Application version: 2.0.0
Database version: v2
Komentarz
Dodając nową kolumnę i kopiując jej zawartość, tworzymy zmiany bazy danych, które są zgodne wstecz. W tym samym czasie, jeśli cofniemy JAR lub mamy działający stary JAR, nie będzie on psuł się podczas wykonywania.
Wydajemy nową wersję
Kroki:
- wykonaj migrację bazy danych, aby stworzyć nową kolumnę
surname. Teraz twoja baza danych wersjiv2 - skopiuj dane z
last_namedosurname. Zwróć uwagę, co jeśli masz dużo tych danych, powinieneś rozważyć migrację wsadową! - napisz kod, w którym używane są OBE i nowy, i stary kolumna. Teraz Twoja aplikacja w wersji
2.0.0 - odczytuje wartość z kolumny
surname, jeśli nie jestnull, lub z last_name, jeślisurnamenie jest ustawione. Możesz usunąćgetLastName()z kodu, ponieważ będzie ona zwracaćnullprzy cofnięciu Twojej aplikacji do3.0.0do2.0.0.
Jeśli używasz Spring Boot Flyway, te dwa kroki będą wykonane podczas uruchamiania wersji 2.0.0 aplikacji. Jeśli uruchamiasz narzędzie do zarządzania wersjami bazy danych ręcznie, będziesz musiał wykonać dwa różne działania (najpierw ręcznie zaktualizować wersję bazy danych, a następnie wdrożyć nową aplikację).
To istotne. Pamiętaj, że nowo utworzona kolumna NIE MOŻE być NOT NULL. Jeśli dokonujesz cofnięcia, starsza aplikacja nie wie o nowej kolumnie i nie ustawi jej podczas
Wstaw.Ale jeśli dodasz to ograniczenie, a Twoja baza danych będziev2, będzie to wymagało ustawienia wartości nowej kolumny. Co doprowadzi do naruszenia ograniczeń.To istotne. Powinieneś usunąć metodę
getLastName(), ponieważ w wersji3.0.0w kodzie nie ma pojęcia kolumnylast_name. Oznacza to, że tam zostaną ustawione wartości null. Możesz pozostawić metodę i dodać sprawdzenia nanull, ale znacznie lepszym rozwiązaniem będzie upewnienie się, że w logicegetSurname()wybrałeś odpowiednią wartość nie-null.
A/B testing
The current situation is that we have an application version 1.0.0, wdrożona na produkcji, a baza danych w v1. Musimy wdrożyć drugi egzemplarz aplikacji w wersji 2.0.0, który zaktualizuje bazę danych do v2.
Kroki:
- a new instance of application version
2.0.0, which updates the database tov2 - w międzyczasie niektóre zapytania były przetwarzane przez egzemplarze wersji
1.0.0 - aktualizacja przebiegła pomyślnie i masz kilka działających egzemplarzy aplikacji wersji
1.0.0oraz inne wersje2.0.0.Wszystkie komunikują się z bazą danych wv2 - wersja
1.0.0nie używa w bazie danych kolumny surname, a wersja2.0.0używa. Nie przeszkadzają sobie nawzajem i nie powinno być błędów. - wersja
2.0.0przechowuje dane zarówno w starej, jak i nowej kolumnie, co zapewnia kompatybilność wsteczną
To istotne. Jeśli masz jakiekolwiek zapytania, które zliczają elementy na podstawie wartości ze starej / nowej kolumny, musisz pamiętać, że teraz masz duplikację wartości (najprawdopodobniej nadal migrują). Na przykład, jeśli chcesz policzyć liczbę użytkowników, których nazwisko (cokolwiek to jest za nazwa kolumny) zaczyna się na literę
A, to przed zakończeniem migracji danych (old→nowykolumna) możesz mieć niespójne dane, jeśli wykonasz zapytanie do nowej kolumny.
Application rollback
Obecnie mamy aplikację w wersji 2.0.0 i bazę danych w v2.
Kroki:
- cofnij swoją aplikację do wersji
1.0.0. - wersja
1.0.0nie używa w bazie danych kolumnysurname, dlatego wycofanie powinno być udane
Zmiany w DB
Baza danych zawiera kolumnę o nazwie last_name.
Oryginalny skrypt Flyway:
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');Skrypt dodawania surname.
Attention. Pamiętaj, że NIE MOŻESZ DODAWAĆ żadnych ograniczeń NOT NULL do dodawanej kolumny. Jeśli cofasz JAR, stara wersja nie ma pojęcia o dodanej kolumnie i automatycznie ustawi ją na NULL. W przypadku istnienia takiego ograniczenia, stara aplikacja po prostu się zepsuje.
-- UWAGA: To pole nie może mieć ograniczenia NOT NULL, ponieważ jeśli wycofasz, stara wersja nie będzie znała tej kolumny
-- i zawsze ustawi ją na NULL
ALTER TABLE PERSON ADD surname varchar(255);
-- ZAKŁADAMY, ŻE JEST TO SZYBKA MIGRACJA - W PRZYPAKU INACZEJ MUSIELIBYŚMY MIGROWAĆ PARTIAMI
UPDATE PERSON SET PERSON.surname = PERSON.last_nameCode changes
Zachowujemy dane zarówno w last_name, jak i w surname. Przy czym odczytujemy z last_name, ponieważ ta kolumna jest najbardziej aktualna. W trakcie wdrażania niektóre zapytania mogły być przetwarzane przez instancję aplikacji, która jeszcze nie została zaktualizowana.
/*
* 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
+ "]";
}
}Krok 3: Usunięcie last_name z kodu
Application version: 3.0.0
Database version:v3
Komentarz
Przyp. red.: Wygląda na to, że w oryginalnym artykule autor błędnie skopiował tekst z tego bloku z kroku 2. Na tym kroku należy wprowadzić zmiany w kodzie aplikacji, mające na celu usunięcie funkcjonalności, która wykorzystuje kolumnę last_name.
Dodając nową kolumnę i kopiując jej zawartość, stworzyliśmy zmiany w bazie danych zgodne wstecz. Także, jeśli wycofamy JAR lub będziemy mieli działający stary JAR, nie zepsuje się on podczas działania.
Application rollback
Obecnie mamy aplikację wersji 3.0.0 i bazę danych v3. Wersja 3.0.0 nie przechowuje danych w last_name. Oznacza to, że w surname przechowywane są najbardziej aktualne informacje.
Kroki:
- cofnij swoją aplikację do wersji
2.0.0. - wersja
2.0.0używa ilast_nameisurname. - wersja
2.0.0weźmiesurname, jeśli nie jest nullem, a w przeciwnym razie —last_name
Database changes
Nie ma strukturalnych zmian w DB. Wykonywany jest następujący skrypt, który przeprowadza ostateczną migrację starych danych:
-- ZAKŁADAMY, ŻE JEST TO SZYBKA MIGRACJA - W PRZYPADKU INACZEJ MUSIELIBYŚMY MIGROWAĆ PARTIAMI
-- TAKŻE NIE SPRAWDZAMY, CZY NIE NADWRACAMY ISTNIEJĄCYCH WPISÓW. MUSIELIBYŚMY PORÓWNAĆ
-- WERSJE WPISÓW, ABY UPEWNIĆ SIĘ, ŻE JEŚLI JEST JUŻ WPIS Z WYŻSZYM NUMEREM WERSJI
-- NIE NADWRACAMY GO.
UPDATE PERSON SET PERSON.surname = PERSON.last_name;
-- USUWANIE OGRANICZENIA NOT NULL; W PRZECIWNYM RAZIE BĘDZIESZ PRÓBOWAŁ WSTAWIĆ WARTOŚĆ NULL LAST_NAME
-- Z OGRANICZENIEM NOT NULL.
ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;Code changes
Przyp. red.: Opis tego bloku również został błędnie skopiowany przez autora z kroku 2. Zgodnie z logiką narracyjną artykułu, zmiany w kodzie na tym etapie powinny być skierowane na usunięcie z niego elementów, które operują na kolumnie last_name.
Przechowujemy dane zarówno w last_name, jak i w surname. Ponadto odczytujemy z kolumny last_name, ponieważ jest to najbardziej aktualne. Podczas wdrażania niektóre zapytania mogą być obsługiwane przez instancję, która nie została jeszcze zaktualizowana.
/*
* 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
+ "]";
}
}Krok 4: Usunięcie last_name z bazy danych
Application version: 4.0.0
Database version: v4
Komentarz
Z powodu, że kod wersji 3.0.0 nie używał kolumny last_name, podczas wykonywania nie wydarzy się nic złego, jeśli wrócimy do 3.0.0 po usunięciu kolumny z bazy danych.
Logi wykonania skryptu
Zrobimy to w następujący sposób:
01) Uruchom 1.0.0
02) Poczekaj na uruchomienie aplikacji (1.0.0)
03) Wygeneruj osobę, wywołując POST localhost:9991/person dla wersji 1.0.0
04) Uruchom 2.0.0
05) Poczekaj na uruchomienie aplikacji (2.0.0)
06) Wygeneruj osobę, wywołując POST localhost:9991/person dla wersji 1.0.0
07) Wygeneruj osobę, wywołując POST localhost:9992/person dla wersji 2.0.0
08) Zatrzymaj aplikację (1.0.0)
09) Uruchom 3.0.0
10) Poczekaj na uruchomienie aplikacji (3.0.0)
11) Wygeneruj osobę, wywołując POST localhost:9992/person dla wersji 2.0.0
12) Wygeneruj osobę, wywołując POST localhost:9993/person dla wersji 3.0.0
13) Zatrzymaj aplikację (3.0.0)
14) Uruchom 4.0.0
15) Poczekaj na uruchomienie aplikacji (4.0.0)
16) Wygeneruj osobę, wywołując POST localhost:9993/person dla wersji 3.0.0
17) Wygeneruj osobę, wywołując POST localhost:9994/person dla wersji 4.0.0
Uruchamianie aplikacji w wersji 1.0.0
Generowanie osoby w wersji 1.0.0
Wysyłanie POST do 127.0.0.1:9991/person. Oto odpowiedź:
{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}
Uruchamianie aplikacji w wersji 2.0.0
Generowanie osoby w wersji 1.0.0
Wysyłanie POST do 127.0.0.1:9991/person. Oto odpowiedź:
{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}
Generowanie osoby w wersji 2.0.0
Wysyłanie POST do 127.0.0.1:9992/person. Oto odpowiedź:
{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}
Zatrzymywanie aplikacji 1.0.0
Uruchamianie aplikacji w wersji 3.0.0
Generowanie osoby w wersji 2.0.0
Wysyłanie POST do 127.0.0.1:9992/person. Oto odpowiedź:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}
Generowanie osoby w wersji 3.0.0
Wysyłanie POST do 127.0.0.1:9993/person. Oto odpowiedź:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}
Zatrzymywanie aplikacji 2.0.0
Uruchamianie aplikacji w wersji 4.0.0
Generowanie osoby w wersji 3.0.0
Wysyłanie POST do 127.0.0.1:9993/person. Oto odpowiedź:
{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}
Generowanie osoby w wersji 4.0.0
Wysyłanie POST do 127.0.0.1:9994/person. Oto odpowiedź:
{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}Zmiany w bazie danych
Odnośnie v3 po prostu usuwamy kolumnę last_name i dodajemy brakujące ograniczenia.
-- USUŃ KOLUMNĘ
ALTER TABLE PERSON DROP last_name;
-- DODAJ OGRANICZENIA
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;Code changes
Zmiany w kodzie są nieobecne.
Wnioski
Sukcesywnie zastosowaliśmy niekompatybilną zmianę nazwy kolumny z powrotem, przeprowadzając kilka zgodnych wdrożeń. Poniżej podsumowanie wykonanych działań:
- wdrożenie aplikacji wersji
1.0.0zv1schemat bazy danych (nazwa kolumny =last_name) - wdrożenie aplikacji wersji
2.0.0,które przechowuje dane wlast_nameisurname. Aplikacja odczytuje zlast_name. Baza danych jest w wersjiv2, zawierającej kolumny takie jaklast_name, jak inazwisko. nazwiskojest kopią last_name. (UWAGA: ta kolumna nie powinna mieć ograniczenia not null) - wdrożenie aplikacji wersji
3.0.0, która przechowuje dane tylko wsurnamei odczytuje z nazwiska. Jeśli chodzi o bazę danych, to odbywa się ostatnia migracjalast_namedosurname. Ponadto ograniczenie NOT NULL zostało usunięte zlast_name. Baza danych jest teraz w wersjiv3 - wdrożenie aplikacji wersji
4.0.0— w kodzie nie wprowadzane są żadne zmiany. Wdrożenie bazy danychv4, która usuwalast_name. Tutaj możesz dodać wszelkie brakujące ograniczenia w bazie danych.
Podążając tym podejściem, zawsze możesz cofnąć się o jedną wersję wstecz, nie łamiąc kompatybilności bazy danych / aplikacji.
Kod
Cały kod użyty w tym artykule jest dostępny na . Poniżej znajduje się dodatkowy opis.
Projekty
Po sklonowaniu repozytorium zobaczysz następującą strukturę folderów.
├── boot-flyway-v1 - wersja 1.0.0 aplikacji z v1 schematu
├── boot-flyway-v2 - wersja 2.0.0 aplikacji z v2 schematu (kompatybilna wstecz - aplikacja może być cofnięta)
├── boot-flyway-v2-bad - wersja 2.0.0.BAD aplikacji z v2bad schematu (niekompatybilna wstecz - aplikacja nie może być cofnięta)
├── boot-flyway-v3 - wersja 3.0.0 aplikacji z v3 schematu (aplikacja może być cofnięta)
└── boot-flyway-v4 - wersja 4.0.0 aplikacji z v4 schematu (aplikacja może być cofnięta)Skrypty
Możesz uruchomić skrypty opisane poniżej, które pokazują zmiany w bazie danych kompatybilne i niekompatybilne wstecz.
Aby zobaczyć przypadek ze zmianami kompatybilnymi wstecz, uruchom:
.\/scripts\/scenario_backward_compatible.shAby zobaczyć przypadek ze zmianami niekompatybilnymi wstecz, uruchom:
.\/scripts\/scenario_backward_incompatible.shPrzykład Spring Boot Flyway
Wszystkie przykłady pochodzą z Przykład Spring Boot Flyway.
Możesz spojrzeć na http://localhost:8080/flyway, tam znajduje się lista skryptów.
Ten przykład zawiera również konsolę H2 (pod adresem http://localhost:8080/h2-console), abyś mógł przeglądać stan bazy danych (domyślny adres URL jdbc — jdbc:h2:mem:testdb).
Dodatkowo
Przeczytaj również inne artykuły na naszym blogu:
Źródło: habr.com
