Nasazení bez výpadků a databáze

Nasazení bez výpadků a databáze

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

Ú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 blue green deployment? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на tento článek, 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í Martin Fowler, 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 Flyway 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.sql

V 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 Spring Boot Docs.

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:

  1. deployed a new instance of the application version 2.0.0.BAD, which updates the database to v2bad
  2. in the database v2bad column last_name no longer exists — it has been changed to surname
  3. the database and application update was successful, and some instances are running in 1.0.0, others in 2.0.0.BAD. All are connected to the DB v2bad
  4. all instances of version 1.0.0 will start throwing errors because they will try to insert data into the column last_name, which no longer exists
  5. all instances of version 2.0.0.BAD will 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:

  1. Zastavujeme instanci aplikace verze 2.0.0.BAD
  2. databáze stále v2bad
  3. protože verze 1.0.0 nechápe, co to je surname, uvidíme chyby
  4. 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 sloupce first_name a last_name. Musíme změnit last_name na surname. Máme také aplikaci verze 1.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:

  1. proveďte migraci DB, abyste vytvořili nový sloupec surname. Nyní vaše DB verze v2
  2. zkopírujte data z last_name v surname. 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!
  3. napište kód, kde používáte OBĚ a nový, a starý sloupec. Nyní vaše aplikace verze 2.0.0
  4. přečtěte hodnotu ze sloupce surname, pokud není null, nebo z last_name, pokud surname není zadáno. Můžete odstranit getLastName() z kódu, protože bude vracet null při návratu vaší aplikace k 3.0.0 do 2.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 bude v2, 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 verzi 3.0.0 v kódu neexistuje pojem sloupec last_name. To znamená, že tam budou nastavena null. Můžete nechat metodu a přidat kontroly na null, ale mnohem lepším řešením by bylo zajistit, že v logice getSurname() 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:

  1. deployed a new instance of the application version 2.0.0, which updates the database to v2
  2. mezitím některé dotazy zpracovávaly instance verze 1.0.0
  3. aktualizace proběhla úspěšně a máte několik funkčních instancí aplikace verze 1.0.0 a ostatní verze 2.0.0. Všechny komunikují s DB v v2
  4. 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.0 nepoužívá ve DB sloupec surname, zatímco verze 2.0.0 používá. Nepřekážejí si navzájem a neměly by být žádné chyby.
  5. 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.0 uchová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:

  1. vrátíte svou aplikaci do verze 1.0.0.
  2. 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.0 nepoužívá ve DB sloupec surname, 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_name

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

  1. vrátíte svou aplikaci do verze 2.0.0.
  2. 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.0 použije a last_name a surname.
  3. 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.0 vezme surname, 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í:

  1. nasazení aplikace verze 1.0.0 s v1 schéma DB (název sloupce = last_name)
  2. nasazení aplikace verze 2.0.0, které uchovává data v last_name a surname. Aplikace čte z last_name. DB je ve verzi v2, která obsahuje sloupce jako last_name, tak i příjmení. příjmení je kopie last_name. (POZNÁMKA: tento sloupec by neměl mít omezení NOT NULL)
  3. nasazení aplikace verze 3.0.0, které uchovává data pouze v surname a čte z příjmení. Co se týče DB, dochází k poslední migraci last_name v surname. Také je odstraněno omezení NOT NULL z last_name. DB je nyní ve verzi v3
  4. nasazení aplikace verze 4.0.0 — v kódu nejsou provedeny žádné změny. Nasazení databáze v4, které odstraňuje last_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 Github. 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.sh

Abychom viděli případ se zpětně nekompatibilními změnami, spusťte:

.\/scripts\/scenario_backward_incompatible.sh

Spring 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

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster