Nulové prostoje nasazení a databází

Nulové prostoje nasazení a databází

Tento článek podrobně vysvětluje, jak vyřešit problémy s kompatibilitou databáze při nasazení. Řekneme vám, co se může stát s vašimi produkčními aplikacemi, pokud se pokusíte o nasazení bez předběžné přípravy. Poté projdeme fázemi životního cyklu aplikace, které jsou nutné pro nulové prostoje (Cca. pruh: dále - nulové prostoje). Výsledkem našich operací bude použití 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 GitHub.

úvod

Nulové prostoje nasazení

Jaké mystické nulové prostoje nasazení? Dá se říci, že je to tehdy, když je vaše aplikace nasazena takovým způsobem, že můžete úspěšně zavést novou verzi aplikace do výroby, přičemž uživatel nezaznamená její nedostupnost. Z pohledu uživatele a společnosti je to nejlepší možný scénář nasazení, protože umožňuje zavádění nových funkcí a opravu chyb bez přerušení.

Jak toho dosáhnout? Existuje několik způsobů, zde je jeden z nich:

  • nasadit verzi č. 1 vaší služby
  • provést migraci databáze
  • Nasaďte verzi #2 vaší služby souběžně s verzí #1
  • jakmile uvidíte, že verze č. 2 funguje jak má, odeberte verzi č. 1
  • hotovo!

Snadné, že? Bohužel to není tak jednoduché a podrobně se na to podíváme později. Nyní se podíváme na další poměrně běžný proces nasazení – modrozelené nasazení.

Už jste někdy slyšeli o modrá zelená nasazení? Cloud Foundry to velmi usnadňuje. Jen se podívejte tento článek, kde to popíšeme podrobněji. Abychom to stručně shrnuli, připomeňme vám, jak provést modrozelené nasazení:

  • zajistit, aby dvě kopie vašeho produkčního kódu („modrá“ a „zelená“) fungovaly;
  • směrovat veškerou dopravu do modrého prostředí, tzn. aby tam produkční URL směřovaly;
  • nasadit a otestovat všechny změny aplikací v zeleném prostředí;
  • přepnout adresy URL z modrého na zelené prostředí

Modrozelené nasazení je přístup, který vám umožňuje snadno zavádět nové funkce bez obav z přerušení výroby. To je způsobeno skutečností, že i když se něco stane, můžete se snadno vrátit do předchozího prostředí pouhým „pohybem přepínače“.

Po přečtení výše uvedeného si můžete položit otázku: Co má nulový prostoj společného s nasazením Blue green?

Mají toho hodně společného, ​​protože udržovat dvě kopie stejného prostředí vyžaduje dvojnásobné úsilí o jejich údržbu. To je důvod, proč některé týmy tvrdí Martin Fowler, použijte variantu tohoto přístupu:

Další možností je použít stejnou databázi a vytvořit modro-zelené přepínače pro webovou a doménovou vrstvu. V tomto přístupu může být databáze často problémem, zvláště když potřebujete změnit její schéma, aby podporovala novou verzi softwaru.

A zde se dostáváme k hlavnímu problému tohoto článku. Databáze. Pojďme se na tuto frázi podívat ještě jednou.

provést migraci databáze.

Nyní si musíte položit otázku – co když změna databáze není zpětně kompatibilní? Nezlomí se moje první verze aplikace? Ve skutečnosti se přesně tohle stane...

Takže i přes obrovské výhody nulových prostojů / modrozeleného nasazení mají společnosti tendenci při nasazování svých aplikací dodržovat následující bezpečnější proces:

  • připravit balíček s novou verzí aplikace
  • vypnout běžící aplikaci
  • spusťte 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 nasazení bez výpadků.

Problémy s databází

Pokud máte bezstavovou aplikaci, která neukládá žádná data do databáze, můžete okamžitě získat nulové prostoje. Bohužel většina softwaru potřebuje někam ukládat data. To je důvod, proč byste si měli dvakrát rozmyslet, než provedete jakékoli změny v obvodu. Než se pustíme do podrobností o tom, jak změnit schéma tak, aby nasazení nebylo možné bez výpadků, zaměřme se nejprve na schéma verzování.

Schéma verzování

V tomto článku použijeme Průlet jako nástroj pro správu verzí (Cca. Překlad: mluvíme o migracích databází). Samozřejmě také napíšeme aplikaci Spring Boot, která má vestavěnou podporu Flyway a bude provádět migraci schématu při nastavování kontextu aplikace. Při používání Flyway můžete ukládat migrační skripty do složky 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.sql

V tomto příkladu vidíme 4 migrační skripty, které, pokud nebyly provedeny dříve, budou provedeny jeden po druhém při spuštění aplikace. Podívejme se na jeden ze 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 je dokonale samovysvětlující: pomocí SQL můžete definovat, jak má být vaše databáze upravena. Pro více informací o Spring Boot a Flyway se podívejte Spring Boot Docs.

Použitím nástroje pro ovládání zdroje s Spring Boot získáte 2 velké výhody:

  • oddělíte změny databáze od změn kódu
  • Migrace databáze probíhá současně s uvedením vaší aplikace, tzn. váš proces nasazení je zjednodušen

Odstraňování problémů s databází

V další části článku se zaměříme na dva přístupy ke změnám databáze.

  • zpětná nekompatibilita
  • zpětná kompatibilita

První bude považováno za varování, že byste neměli provádět nasazení s nulovým prostojem bez předběžné přípravy... Druhý nabízí řešení, jak můžete provést nasazení bez prostojů a zároveň zachovat zpětnou kompatibilitu.

Náš projekt, na kterém budeme pracovat, bude jednoduchá aplikace Spring Boot Flyway, která má Person с first_name и last_name v databázi (Cca. překlad: Person je stůl a first_name и last_name - to jsou pole v něm). Chceme se přejmenovat last_name в surname.

Předpoklady

Než se pustíme do podrobností, musíme si o našich aplikacích udělat pár předpokladů. 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 podpoře (čím více lidí pracuje pro vaši společnost, tím více peněz můžete ušetřit)!

Není nutné vrátit zpět databázi

To zjednodušuje proces nasazení (některá vrácení databází jsou téměř nemožná, jako je například vrácení vrácení smazání). Preferujeme vrátit zpět pouze aplikace. Tímto způsobem, i když máte různé databáze (například SQL a NoSQL), bude váš kanál nasazení vypadat stejně.

VŽDY musí být možné vrátit aplikaci o jednu verzi zpět (ne více)

Vrácení zpět by mělo být provedeno pouze v případě potřeby. Pokud je v aktuální verzi chyba, kterou nelze snadno opravit, měli bychom být schopni vrátit se k nejnovější pracovní verzi. Předpokládáme, že tato nejnovější pracovní verze je předchozí. Udržování kompatibility kódu a databáze pro více než jedno zavedení by bylo extrémně obtížné a nákladné.

Poznámka. Pro větší čitelnost v tomto článku změníme hlavní verzi aplikace.

Krok 1: Počáteční stav

Verze aplikace: 1.0.0
DB verze: v1

Komentář

Toto bude počáteční stav aplikace.

Změny databáze

DB obsahuje 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');

Změny kódu

Aplikace ukládá osobní údaje 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
                + "]";
    }
}

Zpětně nekompatibilní přejmenování sloupců

Podívejme se na příklad, jak změnit název sloupce:

Pozornost. Následující příklad věci záměrně rozbije. Ukážeme to, abychom demonstrovali problém kompatibility databáze.

Verze aplikace: 2.0.0.BAD

DB verze: v2bad

Komentář

Současné změny nám NEUMOŽŇUJÍ spouštět dvě instance (starou a novou) současně. Nasazení nulových prostojů tedy bude obtížné dosáhnout (pokud se vezmou v úvahu předpoklady, je to vlastně nemožné).

A/B testování

Aktuální situace je taková, že máme verzi aplikace 1.0.0, nasazené v produkci a databázi v1. Potřebujeme nasadit druhou instanci aplikace, verzi 2.0.0.BADa aktualizujte databázi na v2bad.

Kroky:

  1. je nasazena nová instance verze aplikace 2.0.0.BADkterý aktualizuje databázi v2bad
  2. v databázi v2bad sloupec last_name již neexistuje – byl změněn na surname
  3. Aktualizace databáze a aplikace byla úspěšná a některé instance jsou spuštěny 1.0.0, ostatní - v 2.0.0.BAD. Vše je propojeno s databází v2bad
  4. všechny instance verze 1.0.0 začnou házet chyby, protože se pokusí vložit data do sloupce last_namekterý už neexistuje
  5. všechny instance verze 2.0.0.BAD bude fungovat bez problémů

Jak vidíte, pokud provedeme zpětně nekompatibilní změny v databázi a aplikaci, A/B testování je nemožné.

Vrácení aplikace

Předpokládejme, že po pokusu o nasazení A/B (Cca. per.: autor zde asi myslel A/B testování) jsme se rozhodli, že musíme aplikaci vrátit zpět na verzi 1.0.0. Řekněme, že nechceme vrátit zpět databázi.

Kroky:

  1. zastavíme verzi instance aplikace 2.0.0.BAD
  2. databáze je stále v2bad
  3. od verze 1.0.0 nerozumí, co to je surname, uvidíme chyby
  4. peklo se rozpoutalo, už se nemůžeme vrátit

Jak vidíte, pokud provedeme zpětně nekompatibilní změny v databázi a aplikaci, nemůžeme se vrátit k předchozí verzi.

Protokoly provádění skriptů

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"}

Změny databáze

Migrační skript, který přejmenuje last_name в surname

Zdrojový 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, který se přejmenuje last_name.

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

Změny kódu

Změnili jsme název pole lastName na surname.

Přejmenování sloupce zpětně kompatibilním způsobem

Toto je nejčastější situace, se kterou se můžeme setkat. Musíme provést zpětně nekompatibilní změny. Již jsme dokázali, že pro nasazení s nulovými prostoji bychom neměli jednoduše aplikovat migraci databáze bez dalších kroků. V této části článku provedeme 3 nasazení aplikace spolu s migrací databáze, abychom dosáhli požadovaného výsledku při zachování zpětné kompatibility.

Poznámka. Připomeňme, že máme databázi verzí v1. Obsahuje sloupce first_name и last_name. Musíme se změnit last_name na surname. Máme také verzi aplikace 1.0.0, která se ještě nepoužívá surname.

Krok 2: Přidejte příjmení

Verze aplikace: 2.0.0
DB verze: v2

Komentář

Přidáním nového sloupce a zkopírováním jeho obsahu vytvoříme zpětně kompatibilní databázové změny. Současně, pokud vrátíme zpět JAR nebo máme spuštěný starý JAR, během provádění se nerozbije.

Vydáváme novou verzi

Kroky:

  1. proveďte migraci databáze a vytvořte nový sloupec surname. Nyní vaše verze DB v2
  2. kopírovat data z last_name в surname. Poznámkaže pokud máte hodně těchto dat, měli byste zvážit dávkovou migraci!
  3. napište kód tam, kde se používají OBDOBÍ и novýA starý sloupec. Nyní vaše verze aplikace 2.0.0
  4. odečtěte hodnotu ze sloupce surname, pokud tomu tak není null, nebo od last_name, pokud surname nespecifikováno. Můžete smazat getLastName() z kódu, protože vypíše null při vrácení zpět z aplikace 3.0.0 na 2.0.0.

Pokud používáte Spring Boot Flyway, budou tyto dva kroky provedeny během spouštění verze 2.0.0 aplikací. Pokud spustíte nástroj pro správu verzí databáze ručně, budete k tomu muset udělat dvě různé věci (nejprve ručně aktualizovat verzi databáze a poté nasadit novou aplikaci).

Je to důležité. Nezapomeňte, že nově vytvořený sloupec NEMĚL BY být NENÍ NULL. Pokud provedete rollback, stará aplikace o novém sloupci neví a během něj jej nenainstaluje Insert. Ale pokud přidáte toto omezení a vaše db bude v2, bude to vyžadovat nastavení hodnoty nového sloupce. Což povede k porušování omezení.

Je to důležité. Měli byste odstranit metodu getLastName(), protože ve verzi 3.0.0 V kódu není žádný koncept sloupce last_name. To znamená, že tam bude nastaveno null. Metodu můžete opustit a přidat kontroly null, ale mnohem lepším řešením by bylo ujistit se, že v logice getSurname() vybrali jste správnou nenulovou hodnotu.

A/B testování

Aktuální situace je taková, že máme verzi aplikace 1.0.0, nasazené v produkci a databáze v v1. Potřebujeme nasadit druhou instanci verze aplikace 2.0.0který aktualizuje databázi v2.

Kroky:

  1. je nasazena nová instance verze aplikace 2.0.0který aktualizuje databázi v2
  2. mezitím byly některé požadavky zpracovány instancemi verzí 1.0.0
  3. aktualizace byla úspěšná a máte několik spuštěných instancí verze aplikace 1.0.0 a další verze 2.0.0. Všichni komunikují s databází v v2
  4. verze 1.0.0 nepoužívá v databázi sloupec příjmení, ale verzi 2.0.0 používá. Navzájem se neruší a nemělo by docházet k žádným chybám.
  5. verze 2.0.0 ukládá data do starého i nového sloupce, což zajišťuje zpětnou kompatibilitu

Je to 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 si uvědomit, že nyní máte duplicitní hodnoty (s největší pravděpodobností stále migrují). Chcete-li například spočítat počet uživatelů, jejichž příjmení (ať už se sloupec nazývá jakkoli) začínalo písmenem Aa poté, dokud nebude migrace dat dokončena (oldnew sloupec) můžete mít nekonzistentní data, pokud zadáte dotaz na nový sloupec.

Vrácení aplikace

Nyní máme verzi aplikace 2.0.0 a databáze v v2.

Kroky:

  1. vrátit aplikaci zpět na verzi 1.0.0.
  2. verze 1.0.0 nepoužívá sloupec v databázi surname, takže vrácení by mělo být úspěšné

Změny DB

Databáze obsahuje sloupec s názvem last_name.

Zdrojový 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');

Přidat skript surname.

Pozornost. Pamatujte, že do sloupce, který přidáváte, NEMŮŽETE PŘIDAT žádná omezení NOT NULL. Pokud vrátíte zpět JAR, stará verze nebude mít ponětí o přidaném sloupci a automaticky jej nastaví na NULL. Pokud existuje takové omezení, stará aplikace se jednoduše rozpadne.

-- 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

Změny kódu

Data ukládáme jako last_namea v surname. Zároveň čteme od last_name, protože tento sloupec je nejrelevantnější. Během procesu nasazení mohly být některé požadavky 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: Odstranění last_name z kódu

Verze aplikace: 3.0.0

DB verze:v3

Komentář

Poznámka per.: Zřejmě v původním článku autor omylem zkopíroval text tohoto bloku z kroku 2. V tomto kroku by měly být provedeny změny v kódu aplikace zaměřené na odstranění funkčnosti, 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í databázové změny. Také, pokud vrátíme zpět JAR nebo máme spuštěný starý JAR, během provádění se nerozbije.

Vrácení aplikace

V současné době máme verzi aplikace 3.0.0 a databáze v3. Verze 3.0.0 neukládá data do last_name. To znamená, že v surname jsou uloženy nejaktuálnější informace.

Kroky:

  1. vrátit aplikaci zpět na verzi 2.0.0.
  2. verze 2.0.0 používá a last_name и surname.
  3. verze 2.0.0 bude trvat surname, pokud to není nula, jinak -last_name

Změny databáze

V databázi nejsou žádné strukturální změny. K provedení konečné migrace starých dat se spustí následující skript:

-- 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;

Změny kódu

Poznámka per.: Popis tohoto bloku byl také autorem omylem zkopírován z kroku 2. V souladu s logikou článku by změny v kódu v tomto kroku měly směřovat k odstranění prvků, které se sloupcem pracují last_name.

Data ukládáme jako last_namea v surname. Navíc čteme ze sloupku last_name, protože je to nejrelevantnější. Během procesu nasazení mohou být některé požadavky zpracovány instancí, která ještě nebyla upgradová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: Odebrání příjmení z databáze

Verze aplikace: 4.0.0

DB verze: v4

Komentář

Vzhledem k tomu, že kód verze 3.0.0 sloupec nepoužil last_name, pokud se vrátíme zpět, během provádění se nic špatného nestane 3.0.0 po odstranění sloupce z databáze.

Protokoly provádění skriptů

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"}

Změny DB

Relativně v3 jen odstraníme sloup last_name a přidat chybějící omezení.

-- 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;

Změny kódu

V kódu nejsou žádné změny.

Výkon

Úspěšně jsme použili zpětně nekompatibilní změnu názvu sloupce provedením několika zpětně kompatibilních nasazení. Níže je uveden souhrn provedených akcí:

  1. nasazení verze aplikace 1.0.0 с v1 schéma databáze (název sloupce = last_name)
  2. nasazení verze aplikace 2.0.0, do kterého se ukládají data last_name и surname. Aplikace čte z last_name. Databáze je ve verzi v2obsahující sloupce jako last_nameA surname. surname je kopií last_name. (POZNÁMKA: Tento sloupec nesmí mít omezení nenulové)
  3. nasazení verze aplikace 3.0.0, který pouze ukládá data surname a čte z příjmení. Co se týče databáze, probíhá poslední migrace last_name в surname. Také omezení NENÍ NULL odstraněno z last_name. Databáze je nyní ve verzi v3
  4. nasazení verze aplikace 4.0.0 - v kódu nejsou provedeny žádné změny. Nasazení databáze v4, který odstraňuje last_name. Zde můžete do databáze přidat jakákoli chybějící omezení.

Při dodržení tohoto přístupu můžete vždy vrátit jednu verzi zpět, aniž byste narušili kompatibilitu databáze/aplikace.

Kód

Veškerý kód použitý v tomto článku je k dispozici na adrese GitHub. Níže je další popis.

projekty

Po naklonování úložiště uvidíte následující strukturu složek.

├── 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)

Skripty

Můžete spustit skripty popsané ve skriptech níže, které budou demonstrovat zpětně kompatibilní a nekompatibilní změny v databázi.

Vidět pouzdro se zpětně kompatibilními změnami, spustit:

./scripts/scenario_backward_compatible.sh

A vidět případ se zpětně nekompatibilními změnami, spustit:

./scripts/scenario_backward_incompatible.sh

Spring Boot Ukázka Flyway

Všechny příklady jsou převzaty z Spring Boot Sample Flyway.

Můžete se podívat na http://localhost:8080/flyway, existuje seznam skriptů.

Tento příklad také zahrnuje konzoli H2 (at http://localhost:8080/h2-console), abyste mohli zobrazit stav databáze (výchozí adresa URL jdbc je jdbc:h2:mem:testdb).

Dále

Přečtěte si také další články na našem blogu:

Zdroj: www.habr.com

Přidat komentář