
V tomto článku podrobně vysvětlíme, jak řešit problémy související s kompatibilitou databází při nasazení. Řekneme, co se může stát s vašimi aplikacemi v produkci, pokud se pokusíte provést nasazení bez předchozí přípravy. Poté projdeme kroky životního cyklu aplikace, které jsou nezbytné pro dosažení nulového času prostojů (pozn. překl.: dále – zero downtime). Výsledkem našich operací bude aplikace zpětně nekompatibilní změny databáze zpětně kompatibilním způsobem.
Pokud chcete porozumět příkladům kódu z článku, najdete je na .
Úvod
Zero downtime deployment
Co to je mystické zero downtime deployment? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.
Jak toho dosáhnout? Existuje několik způsobů, zde je jeden z nich:
- nasadit verzi č. 1 vaší služby
- provést migraci DB
- nasadit verzi č. 2 vaší služby paralelně s verzí č. 1
- jakmile uvidíte, že verze č. 2 funguje správně, odstraňte verzi č. 1
- hotovo!
Snadné, že? Bohužel to není tak jednoduché a později se na to podíváme podrobněji. Ale nyní se pojďme podívat na další velmi běžný proces nasazení – blue green deployment.
Slyšeli jste někdy o ? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на , kde to popisujeme podrobněji. Stručně shrneme, jak provádět blue green deployment:
- zajistit provoz dvou kopií vašeho produkčního kódu („blue“ a „green“);
- směřovat veškerý provoz do blue prostředí, tedy aby adresy URL produkce směřovaly tam;
- nasazovat a testovat všechny změny aplikace v green prostředí;
- přepnout adresy URL z blue na green prostředí
Blue green deployment je přístup, který vám umožňuje snadno zavádět nové funkce, aniž byste se obávali, že produkce selže. To souvisí s tím, že i když se něco stane, můžete se snadno vrátit na předchozí prostředí, jednoduše „přepnutím přepínače“.
Poté, co jste přečetli vše výše uvedené, se můžete ptát: Jaký má zero downtime vztah k blue green nasazení?
Nu, mají toho dost společného, protože podpora dvou kopií stejného prostředí vyžaduje dvojnásobné úsilí na jejich údržbu. Proto se některé týmy, jak tvrdí , drží variace tohoto přístupu:
Další možností je využít stejnou databázi a vytvořit modro-zelené přepínače pro webové a doménové vrstvy. V takovém přístupu mohou databáze často představovat problém, zejména když potřebujete změnit její schéma pro podporu nové verze softwaru.
A zde se dostáváme k hlavnímu problému tohoto článku. Databáze. Podívejme se znovu na tuto frázi.
proveďte migraci databáze.
Nyní byste se měli ptát sami sebe — co když je změna databáze zpětně nekompatibilní? Nezhroutí se moje první verze aplikace? Opravdu, to se stane...
Takže i přes obrovské výhody zero downtime / blue green deployment, společnosti mají tendenci dodržovat následující bezpečnější proces nasazování svých aplikací:
- připravit balíček s novou verzí aplikace
- vypnout běžící aplikaci
- spustit skripty pro migraci databáze
- nasadit a spustit novou verzi aplikace
V tomto článku podrobně popíšeme, jak můžete pracovat s databází a kódem, abyste využili výhody zero downtime deployment.
Problémy s databází
Pokud máte stateless aplikaci, která neuchovává žádná data v databázi, můžete dosáhnout zero downtime deployment okamžitě. Bohužel většina softwaru musí někde uchovávat data. Proto byste se měli dvakrát zamyslet, než provedete jakékoli změny ve schématu. Než se podrobně zaměříme na to, jak změnit schéma tak, aby bylo možné nasazení bez prostojů, soustřeďme se nejprve na schéma správy verzí.
Schéma správy verzí
V tomto článku budeme používat jako nástroj pro správu verzí (pozn. př.: jedná se o migrace databáze). Přirozeně také napíšeme aplikaci Spring Boot, která má vestavěnou podporu Flyway a vykoná migraci schématu během nastavení kontextu aplikace. Při používání Flyway můžete uchovávat migrační skripty ve složce vašich projektů (ve výchozím nastavení v classpath:db/migration). Zde můžete vidět příklad takových migračních souborů
└── db
└── migration
├── V1__init.sql
├── V2__Add_surname.sql
├── V3__Final_migration.sql
└── V4__Remove_lastname.sqlV tomto příkladu vidíme 4 scénáře migrace, které, pokud nebyly dříve provedeny, budou prováděny jeden po druhém při spuštění aplikace. Podívejme se na jeden z souborů (V1__init.sql) jako příklad.
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');Vše jasně mluví samo za sebe: můžete použít SQL k určení, jak by měla být vaše databáze změněna. Pro více informací o Spring Boot a Flyway se podívejte na .
Použitím nástroje pro řízení verzí se Spring Boot získáte 2 velké výhody:
- oddělujete změny databáze od změn kódu
- migrace databáze se provádí spolu s nasazením vaší aplikace, tedy váš proces nasazení se zjednodušuje.
Řešení problémů s databází
V následující části článku se zaměříme na dvě přístupy k změnám databáze.
- zpětná nekompatibilita
- zpětná kompatibilita
První přístup bude považován za varování, že byste neměli provádět nasazení s nulovým výpadkem bez předchozí přípravy… Druhý nabízí řešení, jak provést nasazení bez výpadků a zároveň zachovat zpětnou kompatibilitu.
Náš projekt, na kterém budeme pracovat, bude jednoduchá aplikace Spring Boot Flyway, ve které je Person s first_name a last_name v databázi (pozn. překl.: Person je tabulka a first_name a last_name to jsou pole v ní). Chceme přejmenovat last_name v surname.
Předpoklady
Než se ponoříme do podrobností, je třeba uvést pár předpokladů ohledně našich aplikací. Hlavním výsledkem, kterého chceme dosáhnout, bude poměrně jednoduchý proces.
Poznámka. Business PRO-TIP. Zjednodušení procesů vám může ušetřit spoustu peněz na údržbě (čím více lidí pracuje ve vaší společnosti, tím více peněz můžete ušetřit)!
Není třeba dělat rollback databáze
To zjednodušuje proces nasazení (některé rollbacks databáze jsou prakticky nemožné, například rollback odstranění). Preferujeme rollback pouze aplikací. Tímto způsobem, i když máte různé databáze (například SQL a NoSQL), bude váš deployment pipeline vypadat stejně.
Musí existovat možnost vždy vrátit aplikaci o jednu verzi zpět (ne více).
Rollback should only be performed when necessary. If there is a bug in the current version that is difficult to resolve, we must be able to revert to the last working version. We assume that this last working version is the previous one. Supporting code and database compatibility for more than one rollout would be extremely challenging and expensive.
Poznámka. For better readability, in this article we will change the major version of the application.
Step 1: Initial state
Application version: 1.0.0
DB version: v1
Komentář
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
+ "]";
}
}Backward incompatible renaming of a column
Let's look at an example of how to change the name of a column:
Attention. The following example will intentionally lead to a break. We demonstrate this to highlight the database compatibility issue.
Application version: 2.0.0.BAD
DB version: v2bad
Komentář
Current changes do NOT allow us to run two instances (old and new) simultaneously. Thus, zero downtime deployment will be difficult to achieve (considering 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 DB v1. We need to deploy a second instance of the application, version 2.0.0.BAD, and update the database to v2bad.
Kroky:
- deployed a new instance of the application version
2.0.0.BAD, which updates the database tov2bad - in the database
v2badcolumnlast_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 DBv2bad - 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 backward incompatible changes to the DB and application, A/B testing becomes impossible.
Application rollback
Let's assume that after attempting to perform an A/B deployment (author probably meant A/B testing here) we concluded that we need to roll back the application to version 1.0.0. Let's say we don't want to rollback the database.
Kroky:
- Zastavujeme instanci aplikace verze
2.0.0.BAD - databáze stále
v2bad - protože verze
1.0.0nechápe, co to jesurname, uvidíme chyby - peklo se uvolnilo, už se nemůžeme vrátit
Jak vidíte, pokud provedeme zpětně nekompatibilní změny v DB a aplikaci, nemůžeme se vrátit k předchozí verzi.
Logy spuštění skriptu
Scénář zpětné nekompatibility:
01) Spusťte 1.0.0
02) Čekejte, až se aplikace (1.0.0) načte
03) Vygenerujte osobu voláním POST localhost:9991/person na verzi 1.0.0
04) Spusťte 2.0.0.BAD
05) Čekejte, až se aplikace (2.0.0.BAD) načte
06) Vygenerujte osobu voláním POST localhost:9991/person na verzi 1.0.0 <--- to by mělo selhat
07) Vygenerujte osobu voláním POST localhost:9992/person na verzi 2.0.0.BAD <--- to by mělo projít
Spouštění aplikace ve verzi 1.0.0
Vygenerujte osobu ve verzi 1.0.0
Odesílání POST na 127.0.0.1:9991/person. Toto je odpověď:
{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}
Spouštění aplikace ve verzi 2.0.0.BAD
Vygenerujte osobu ve verzi 1.0.0
Odesílání POST na 127.0.0.1:9991/person. Toto je odpověď:
curl: (22) Požadovaná adresa URL vrátila chybu: 500 Interní serverová chyba
Vygenerujte osobu ve verzi 2.0.0.BAD
Odesílání POST na 127.0.0.1:9995/person. Toto je odpověď:
{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}Database changes
Skript migrace, který přejmenovává last_name v surname
Původní 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');Skript, který přejmenovává last_name.
-- Tato změna je zpětně nekompatibilní - nemůžete provádět A/B testování
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;Code changes
Změnili jsme název pole lastName na surname.
Přejmenování sloupce zpětně kompatibilním způsobem
Tohle je nejběžnější situace, se kterou se můžeme setkat. Musíme provést zpětně nekompatibilní změny. Již jsme prokázali, že pro nasazení bez prostojů nesmíme pouze aplikovat migraci databáze bez dalších kroků. V této části článku provedeme 3 nasazení aplikace spolu s migracemi databáze, abychom dosáhli požadovaného výsledku a zároveň si zachovali zpětnou kompatibilitu.
Poznámka. Připomeňme, že máme DB verze
v1. Obsahuje sloupcefirst_namealast_name. Musíme změnitlast_namenasurname. Máme také aplikaci verze1.0.0,která zatím nepoužívásurname.
Krok 2: Přidání surname
Application version: 2.0.0
DB version: v2
Komentář
Přidáním nového sloupce a kopírováním jeho obsahu vytváříme zpětně kompatibilní změny DB. Současně, pokud vrátíme JAR nebo máme fungující starý JAR, nebude to během provádění selhávat.
Nasazujeme novou verzi
Kroky:
- proveďte migraci DB, abyste vytvořili nový sloupec
surname. Nyní vaše DB verzev2 - zkopírujte data z
last_namevsurname. Vezměte prosím na vědomí, co byste měli udělat, pokud máte hodně těchto dat, měli byste zvážit dávkovou migraci! - napište kód, kde používáte OBĚ a nový, a starý sloupec. Nyní vaše aplikace verze
2.0.0 - přečtěte hodnotu ze sloupce
surname, pokud nenínull, nebo z last_name, pokudsurnamenení zadáno. Můžete odstranitgetLastName()z kódu, protože bude vracetnullpři návratu vaší aplikace k3.0.0do2.0.0.
Pokud používáte Spring Boot Flyway, tyto dva kroky budou provedeny při startu verze 2.0.0 aplikace. Pokud spouštíte nástroj pro správu verzí databáze ručně, budete muset provést dvě různé akce (nejprve aktualizujte verzi databáze ručně a poté nasadit novou aplikaci).
Důležité. Pamatujte, že nově vytvořený sloupec NESMÍ být NOT NULL. Pokud děláte návrat, stará aplikace neví o novém sloupci a nenastaví ho během
Insert.Ale pokud přidáte toto omezení, a vaše DB budev2, bude to vyžadovat nastavení hodnoty nového sloupce. Což povede k porušení omezení.Důležité. Měli byste odstranit metodu
getLastName(), protože v verzi3.0.0v kódu neexistuje pojem sloupeclast_name. To znamená, že tam budou nastavena null. Můžete nechat metodu a přidat kontroly nanull, ale mnohem lepším řešením by bylo zajistit, že v logicegetSurname()jste vybrali správnou nenulovou hodnotu.
A/B testing
The current situation is that we have an application version 1.0.0, nasazenou na produkci, a DB v v1. Musíme nasadit druhý instance aplikace verze 2.0.0, který aktualizuje databázi na v2.
Kroky:
- deployed a new instance of the application version
2.0.0, which updates the database tov2 - mezitím některé dotazy zpracovávaly instance verze
1.0.0 - aktualizace proběhla úspěšně a máte několik funkčních instancí aplikace verze
1.0.0a ostatní verze2.0.0.Všechny komunikují s DB vv2 - webu ICQ.com datovaná v dubnu 1997, a tehdy doména patřila zcela jiné organizaci — nějaké asociaci výrobců a uživatelů měřicí techniky. V
1.0.0nepoužívá ve DB sloupec surname, zatímco verze2.0.0používá. Nepřekážejí si navzájem a neměly by být žádné chyby. - webu ICQ.com datovaná v dubnu 1997, a tehdy doména patřila zcela jiné organizaci — nějaké asociaci výrobců a uživatelů měřicí techniky. V
2.0.0uchovává data jak ve starém, tak v novém sloupci, což zajišťuje zpětnou kompatibilitu
Důležité. Pokud máte nějaké dotazy, které počítají položky na základě hodnot ze starého / nového sloupce, měli byste mít na paměti, že nyní máte duplicity hodnot (s největší pravděpodobností se stále migrace). Například, pokud chcete spočítat počet uživatelů, jejichž příjmení (jak se sloupec nazývá) začínalo písmenem
A, do dokončení migrace dat (old→novýsloupec) můžete mít nekonzistentní data, pokud provedete dotaz na nový sloupec.
Application rollback
Nyní máme aplikaci verze 2.0.0 a databázi v v2.
Kroky:
- vrátíte svou aplikaci do verze
1.0.0. - webu ICQ.com datovaná v dubnu 1997, a tehdy doména patřila zcela jiné organizaci — nějaké asociaci výrobců a uživatelů měřicí techniky. V
1.0.0nepoužívá ve DB sloupecsurname, proto by měl být úspěšný návrat
Změny DB
DB obsahuje sloupec s názvem last_name.
Původní skript 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');Skript pro přidání surname.
Attention. Pamatujte, že NENÍ MŮŽNÉ PŘIDAT jakákoliv omezení NOT NULL do přidaného sloupce. Pokud vracíte JAR, stará verze nebude mít žádnou představu o přidaném sloupci a automaticky mu přiřadí hodnotu NULL. V případě existence takového omezení se stará aplikace jednoduše rozbije.
-- POZNÁMKA: Toto pole nemůže mít omezení NOT NULL, protože pokud se vrátíte zpět, stará verze nebude o tomto poli vědět
-- a vždy mu přiřadí hodnotu NULL
ALTER TABLE PERSON ADD surname varchar(255);
-- PŘEDPOKLÁDÁME, ŽE JE TO RYCHLÁ MIGRACE - JINAK BYCHOM MUSILI MIGROVAT V DÁLKÁCH
UPDATE PERSON SET PERSON.surname = PERSON.last_nameCode changes
Ukládáme data jako v last_name, tak i v surname. Při tom čteme z last_name, protože tento sloupec je nejaktuálnější. Během nasazení mohly být některé dotazy zpracovány instancí aplikace, která ještě nebyla aktualizována.
/*
* 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: Odebrání last_name z kódu
Application version: 3.0.0
DB version:v3
Komentář
Pozn. př.: Zřejmě byl v původním článku autorem chybně zkopírován text tohoto bloku ze kroku 2. V tomto kroku by měly být provedeny změny v kódu aplikace zaměřené na odstranění funkcionality, která používá sloupec last_name.
Přidáním nového sloupce a zkopírováním jeho obsahu jsme vytvořili zpětně kompatibilní změny DB. Také, pokud vrátíme JAR nebo budeme mít funkční starý JAR, nebude se během provádění rozbíjet.
Application rollback
V současnosti máme aplikaci verze 3.0.0 a databázi v3. Verze 3.0.0 neukládá data do last_name. To znamená, že v surname se uchovává nejaktuálnější informace.
Kroky:
- vrátíte svou aplikaci do verze
2.0.0. - webu ICQ.com datovaná v dubnu 1997, a tehdy doména patřila zcela jiné organizaci — nějaké asociaci výrobců a uživatelů měřicí techniky. V
2.0.0použije alast_nameasurname. - webu ICQ.com datovaná v dubnu 1997, a tehdy doména patřila zcela jiné organizaci — nějaké asociaci výrobců a uživatelů měřicí techniky. V
2.0.0vezmesurname, pokud není nulový, jinak —last_name
Database changes
V DB nejsou strukturální změny. Provádí se následující skript, který provádí konečnou migraci starých dat:
-- PŘEDPOKLÁDÁME, ŽE JE TO RYCHLÁ MIGRACE - JINAK BYCHOM MUSILI MIGROVAT V DÁLKÁCH
-- TAKÉ NEVALIDUJEME, ZDA NEPŘEKRÝVÁME EXISTUJÍCÍ ZÁZNAMY. MUSILI BYCHOM POROVNAT
-- VERSIE ZÁZNAMŮ, ABYCHOM ZAJISTILI, ŽE POKUD UŽ EXISTUJE ZÁZNAM S VYŠŠÍM ČÍSLEM VERZE
-- NEPŘEKRYJEME HO.
UPDATE PERSON SET PERSON.surname = PERSON.last_name;
-- ODSTRANĚNÍ OMEZENÍ NOT NULL; JINAK SE POKUSÍTE VLOŽIT NULLOVOU HODNOTU LAST_NAME
-- S OMEZENÍM NOT NULL.
ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;Code changes
Pozn. př.: Popis tohoto bloku byl také chybně zkopírován autorem z kroku 2. V souladu s logikou článku by změny v kódu v tomto kroku měly být zaměřeny na odstranění prvků, které pracují se sloupcem last_name.
Ukládáme data jako v last_name, tak i v surname. Kromě toho čteme ze sloupce last_name, protože je nejrelevantnější. Během nasazování mohou některé dotazy být zpracovány instancí, která ještě nebyla aktualizována.
/*
* 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: Odstranění last_name z DB
Application version: 4.0.0
DB version: v4
Komentář
Vzhledem k tomu, že kód verze 3.0.0 nepoužil sloupec last_name, během provádění nedojde k žádným problémům, pokud se vrátíme zpět na 3.0.0 po odstranění sloupce z databáze.
Logy spuštění skriptu
Provedeme to následujícím způsobem:
01) Spusťte 1.0.0
02) Počkejte, až se aplikace (1.0.0) spustí
03) Vytvořte osobu zavoláním POST localhost:9991/person na verzi 1.0.0
04) Spusťte 2.0.0
05) Počkejte, až se aplikace (2.0.0) spustí
06) Vytvořte osobu zavoláním POST localhost:9991/person na verzi 1.0.0
07) Vytvořte osobu zavoláním POST localhost:9992/person na verzi 2.0.0
08) Zastavte aplikaci (1.0.0)
09) Spusťte 3.0.0
10) Počkejte, až se aplikace (3.0.0) spustí
11) Vytvořte osobu zavoláním POST localhost:9992/person na verzi 2.0.0
12) Vytvořte osobu zavoláním POST localhost:9993/person na verzi 3.0.0
13) Zastavte aplikaci (3.0.0)
14) Spusťte 4.0.0
15) Počkejte, až se aplikace (4.0.0) spustí
16) Vytvořte osobu zavoláním POST localhost:9993/person na verzi 3.0.0
17) Vytvořte osobu zavoláním POST localhost:9994/person na verzi 4.0.0
Spouštění aplikace ve verzi 1.0.0
Vytváření osoby ve verzi 1.0.0
Odesílání POST na 127.0.0.1:9991/person. Toto je odpověď:
{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}
Spouštění aplikace ve verzi 2.0.0
Vytváření osoby ve verzi 1.0.0
Odesílání POST na 127.0.0.1:9991/person. Toto je odpověď:
{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}
Vytváření osoby ve verzi 2.0.0
Odesílání POST na 127.0.0.1:9992/person. Toto je odpověď:
{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}
Zastavení aplikace 1.0.0
Spouštění aplikace ve verzi 3.0.0
Vytváření osoby ve verzi 2.0.0
Odesílání POST na 127.0.0.1:9992/person. Toto je odpověď:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}
Vytváření osoby ve verzi 3.0.0
Odesílání POST na 127.0.0.1:9993/person. Toto je odpověď:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}
Zastavení aplikace 2.0.0
Spouštění aplikace ve verzi 4.0.0
Vytváření osoby ve verzi 3.0.0
Odesílání POST na 127.0.0.1:9993/person. Toto je odpověď:
{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}
Vytváření osoby ve verzi 4.0.0
Odesílání POST na 127.0.0.1:9994/person. Toto je odpověď:
{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}Změny DB
Pokud jde o v3 prostě odstraňujeme sloupec last_name a přidáváme chybějící omezení.
-- ODSTRANIT SLUPOK
ALTER TABLE PERSON DROP last_name;
-- PŘIDAT OMEZENÍ
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;Code changes
Žádné změny v kódu.
Výstup
Úspěšně jsme vrátili nekompatibilní změnu názvu sloupce prostřednictvím několika zpětně kompatibilních nasazení. Níže je shrnutí provedených akcí:
- nasazení aplikace verze
1.0.0sv1schéma DB (název sloupce =last_name) - nasazení aplikace verze
2.0.0,které uchovává data vlast_nameasurname. Aplikace čte zlast_name. DB je ve verziv2, která obsahuje sloupce jakolast_name, tak ipříjmení. příjmeníje kopie last_name. (POZNÁMKA: tento sloupec by neměl mít omezení NOT NULL) - nasazení aplikace verze
3.0.0, které uchovává data pouze vsurnamea čte z příjmení. Co se týče DB, dochází k poslední migracilast_namevsurname. Také je odstraněno omezení NOT NULL zlast_name. DB je nyní ve verziv3 - nasazení aplikace verze
4.0.0— v kódu nejsou provedeny žádné změny. Nasazení databázev4, které odstraňujelast_name. Zde můžete přidat jakákoli chybějící omezení do DB.
Tímto přístupem se vždy můžete vrátit zpět na jednu verzi, aniž byste rozbili kompatibilitu databáze / aplikace.
Kód
Veškerý kód použitý v tomto článku je k dispozici na . Níže je uveden další popis.
Projekty
Po zkopírování repozitáře uvidíte následující strukturu složek.
├── boot-flyway-v1 - verze 1.0.0 aplikace s v1 schématu
├── boot-flyway-v2 - verze 2.0.0 aplikace s v2 schématu (zpětně kompatibilní - aplikaci lze vrátit zpět)
├── boot-flyway-v2-bad - verze 2.0.0.BAD aplikace s v2bad schématu (zpětně nekompatibilní - aplikaci nelze vrátit zpět)
├── boot-flyway-v3 - verze 3.0.0 aplikace s v3 schématu (aplikaci lze vrátit zpět)
└── boot-flyway-v4 - verze 4.0.0 aplikace s v4 schématu (aplikaci lze vrátit zpět)Skripty
Můžete spustit skripty popsané níže, které předvedou zpětně kompatibilní a nekompatibilní změny v DB.
Abyste viděli případ se zpětně kompatibilními změnami, spusťte:
.\/scripts\/scenario_backward_compatible.shAbychom viděli případ se zpětně nekompatibilními změnami, spusťte:
.\/scripts\/scenario_backward_incompatible.shSpring Boot Sample Flyway
Všechny příklady byly převzaty z Spring Boot Sample Flyway.
Můžete se podívat na http://localhost:8080/flyway, kde je seznam skriptů.
Tento příklad také zahrnuje konzoli H2 (na adrese http://localhost:8080/h2-console), abyste mohli prohlížet stav databáze (výchozí URL jdbc — jdbc:h2:mem:testdb).
Dodatečně
Také si přečtěte další články na našem blogu:
Zdroj: habr.com
