Zero Downtime Deployment i baze podataka

Zero Downtime Deployment i baze podataka

Ovaj članak detaljno objašnjava kako riješiti probleme s kompatibilnošću baze podataka u implementaciji. Reći ćemo vam šta se može dogoditi vašim proizvodnim aplikacijama ako pokušate da se implementirate bez preliminarne pripreme. Zatim ćemo proći kroz faze životnog ciklusa aplikacije koje su potrebne da nemaju zastoja (cca. traka: dalje - nula zastoja). Rezultat naših operacija će biti primjena nazad nekompatibilne promjene baze podataka na kompatibilan način.

Ako želite razumjeti primjere koda iz članka, možete ih pronaći na GitHub.

Uvod

Postavljanje bez zastoja

Kakva mistika nula zastoja u implementaciji? Možete reći da je to kada je vaša aplikacija raspoređena na način da možete uspješno uvesti novu verziju aplikacije u proizvodnju, a da korisnik ne primijeti njenu nedostupnost. Iz perspektive korisnika i kompanije, ovo je najbolji mogući scenarij implementacije jer omogućava uvođenje novih funkcija i ispravljanje grešaka bez ometanja.

Kako to postići? Postoji nekoliko načina, evo jednog od njih:

  • implementirajte verziju br. 1 svoje usluge
  • izvršite migraciju baze podataka
  • Uvedite verziju #2 svoje usluge paralelno sa verzijom #1
  • čim vidite da verzija br. 2 radi kako treba, uklonite verziju br. 1
  • gotov!

Lako, zar ne? Nažalost, to nije tako jednostavno, a kasnije ćemo to detaljnije pogledati. Sada provjerimo još jedan prilično uobičajen proces implementacije - plavo zeleno postavljanje.

Jeste li ikada čuli za plavo zeleno raspoređivanje? Cloud Foundry ovo čini izuzetno lakim. Samo pogledajte ovaj članak, gdje ovo detaljnije opisujemo. Da ukratko rezimiramo, podsjetimo vas kako napraviti plavo zelenu implementaciju:

  • osigurajte da dvije kopije vašeg proizvodnog koda („plava“ i „zelena“) rade;
  • usmjeriti sav saobraćaj u plavo okruženje, tj. tako da proizvodni URL-ovi ukazuju tamo;
  • implementirati i testirati sve promjene aplikacija u zelenom okruženju;
  • prebaciti URL iz plavog u zeleno okruženje

Plavo zelena implementacija je pristup koji vam omogućava da lako uvedete nove funkcije bez brige o prekidu proizvodnje. To je zbog činjenice da čak i ako se nešto dogodi, lako se možete vratiti u prethodno okruženje jednostavnim „prebacivanjem prekidača“.

Nakon što pročitate sve gore navedeno, možete postaviti pitanje: Kakve veze ima nulti prekid rada sa plavo-zelenom implementacijom?

Pa, imaju dosta toga zajedničkog, budući da održavanje dvije kopije istog okruženja zahtijeva dvostruki napor da se one održe. To je razlog zašto neki timovi tvrde Martin Fowler, slijedite varijaciju ovog pristupa:

Druga opcija je korištenje iste baze podataka, kreiranje plavo-zelenih prekidača za web i slojeve domena. U ovom pristupu, baza podataka često može predstavljati problem, posebno kada trebate promijeniti njenu shemu kako biste podržali novu verziju softvera.

I tu dolazimo do glavnog problema u ovom članku. Baza podataka. Pogledajmo još jednom ovu frazu.

izvršite migraciju baze podataka.

Sada morate sebi postaviti pitanje - šta ako promjena baze podataka nije kompatibilna unatrag? Neće li se moja prva verzija aplikacije pokvariti? U stvari, upravo ovo će se desiti...

Dakle, uprkos ogromnim prednostima nulte zastoja/plavo zelene implementacije, kompanije obično slijede sljedeći sigurniji proces za implementaciju svojih aplikacija:

  • pripremite paket sa novom verzijom aplikacije
  • isključite pokrenutu aplikaciju
  • pokrenite skripte za migraciju baze podataka
  • implementirati i pokrenuti novu verziju aplikacije

U ovom članku ćemo detaljno opisati kako možete raditi sa svojom bazom podataka i kodom kako biste iskoristili prednosti implementacije bez prekida.

Problemi sa bazom podataka

Ako imate aplikaciju bez stanja koja ne pohranjuje nikakve podatke u bazi podataka, odmah možete dobiti nultu implementaciju zastoja. Nažalost, većina softvera mora negdje pohraniti podatke. Ovo je razlog zašto biste trebali dvaput razmisliti prije nego što napravite bilo kakve promjene u krugu. Prije nego što uđemo u detalje o tome kako promijeniti shemu tako da je moguća implementacija bez zastoja, hajde da se prvo usredsredimo na šemu verzioniranja.

Šema verzija

U ovom članku ćemo koristiti Flyway kao alat za kontrolu verzija (cca. per.: govorimo o migracijama baze podataka). Naravno, takođe ćemo napisati Spring Boot aplikaciju koja ima ugrađenu Flyway podršku i koja će vršiti migraciju šeme dok postavlja kontekst aplikacije. Kada koristite Flyway, možete pohraniti skripte za migraciju u direktorij projekata (podrazumevano u classpath:db/migration). Ovdje možete vidjeti primjer takvih datoteka za migraciju

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

U ovom primjeru vidimo 4 skripte za migraciju koje će se, ako se prethodno ne izvrše, izvršavati jedna za drugom kada se aplikacija pokrene. Pogledajmo jedan od fajlova (V1__init.sql) kao primjer.

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

Sve je savršeno samo po sebi razumljivo: možete koristiti SQL da definirate kako vaša baza podataka treba biti modificirana. Za više informacija o Spring Boot i Flywayu pogledajte Spring Boot Docs.

Korišćenjem alata za kontrolu izvora sa Spring Boot-om, dobijate 2 velike prednosti:

  • odvajate promjene baze podataka od promjena koda
  • Migracija baze podataka se dešava zajedno sa uvođenjem vaše aplikacije, tj. vaš proces implementacije je pojednostavljen

Rješavanje problema s bazom podataka

U sljedećem dijelu članka fokusirat ćemo se na razmatranje dva pristupa promjenama baze podataka.

  • nazadna nekompatibilnost
  • kompatibilnost unatrag

Prvi će se smatrati upozorenjem da ne smijete izvoditi implementaciju bez zastoja bez preliminarne pripreme... Drugi nudi rješenje kako možete izvesti deployment bez zastoja i istovremeno održati kompatibilnost unatrag.

Naš projekat na kojem ćemo raditi bit će jednostavna Spring Boot Flyway aplikacija koja ima Person с first_name и last_name u bazi podataka (cca. prijevod: Person je sto i first_name и last_name - ovo su polja u njemu). Želimo preimenovati last_name в surname.

Pretpostavke

Prije nego što uđemo u detalje, postoji nekoliko pretpostavki koje moramo napraviti o našim aplikacijama. Glavni rezultat koji želimo postići bit će prilično jednostavan proces.

Napomena. Poslovni PRO-TIP. Pojednostavljivanje vaših procesa može vam uštedjeti mnogo novca na podršci (što više ljudi radite za vašu kompaniju, više novca možete uštedjeti)!

Nema potrebe za vraćanjem baze podataka

Ovo pojednostavljuje proces implementacije (neka vraćanja baze podataka su gotovo nemoguća, kao što je vraćanje brisanja). Radije vraćamo samo aplikacije. Na ovaj način, čak i ako imate različite baze podataka (na primjer, SQL i NoSQL), vaš cevovod za implementaciju će izgledati isto.

UVIJEK mora biti moguće vratiti aplikaciju za jednu verziju unazad (ne više)

Vraćanje se treba izvršiti samo kada je to potrebno. Ako postoji greška u trenutnoj verziji koju nije lako popraviti, trebali bismo biti u mogućnosti da se vratimo na najnoviju radnu verziju. Pretpostavljamo da je ova najnovija radna verzija prethodna. Održavanje kompatibilnosti koda i baze podataka za više od jednog uvođenja bilo bi izuzetno teško i skupo.

Napomena. Radi veće čitljivosti, u ovom članku ćemo promijeniti glavnu verziju aplikacije.

Korak 1: Početno stanje

Verzija aplikacije: 1.0.0
DB verzija: v1

komentar

Ovo će biti početno stanje aplikacije.

Promjene u bazi podataka

DB sadrži 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');

Promjene koda

Aplikacija pohranjuje podatke o ličnosti u 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
                + "]";
    }
}

Preimenovanje kolone unatrag nekompatibilno

Pogledajmo primjer kako promijeniti naziv stupca:

Pažnja. Sljedeći primjer će namjerno razbiti stvari. Ovo pokazujemo kako bismo demonstrirali problem kompatibilnosti baze podataka.

Verzija aplikacije: 2.0.0.BAD

DB verzija: v2bad

komentar

Trenutne promjene nam NE dozvoljavaju da istovremeno pokrećemo dvije instance (stare i nove). Stoga će biti teško postići implementaciju bez zastoja (ako se uzmu u obzir pretpostavke, to je zapravo nemoguće).

A/B testiranje

Trenutna situacija je da imamo verziju aplikacije 1.0.0, raspoređeni u proizvodnji i bazi podataka v1. Moramo implementirati drugu instancu aplikacije, verziju 2.0.0.BAD, i ažurirajte bazu podataka na v2bad.

Koraci:

  1. postavlja se nova instanca aplikacije verzije 2.0.0.BADkoji ažurira bazu podataka na v2bad
  2. u bazi podataka v2bad stupac last_name više ne postoji - promijenjeno je u surname
  3. Ažuriranje baze podataka i aplikacije je uspješno i neke instance su pokrenute 1.0.0, ostali - u 2.0.0.BAD. Sve je povezano sa bazom podataka v2bad
  4. sve instance verzije 1.0.0 će početi bacati greške jer će pokušati umetnuti podatke u kolonu last_namekoji više ne postoji
  5. sve instance verzije 2.0.0.BAD će raditi bez problema

Kao što vidite, ako izvršimo unazad nekompatibilne promjene u bazi podataka i aplikaciji, A/B testiranje je nemoguće.

Vraćanje aplikacije

Pretpostavimo da nakon pokušaja A/B implementacije (cca. per.: autor je ovdje vjerovatno mislio na A/B testiranje) odlučili smo da moramo vratiti aplikaciju na verziju 1.0.0. Recimo da ne želimo da vratimo bazu podataka.

Koraci:

  1. zaustavljamo verziju aplikacije instance 2.0.0.BAD
  2. baza podataka je i dalje v2bad
  3. od verzije 1.0.0 ne razume šta je to surname, vidjet ćemo greške
  4. pakao je izbio, ne možemo se više vratiti

Kao što vidite, ako izvršimo unazad nekompatibilne promjene u bazi podataka i aplikaciji, ne možemo se vratiti na prethodnu verziju.

Dnevnici izvršavanja skripte

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

Promjene u bazi podataka

Skripta za migraciju koja preimenuje last_name в surname

Izvor Flyway skripte:

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

Skripta koja preimenuje last_name.

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

Promjene koda

Promijenili smo naziv polja lastName na surname.

Preimenovanje stupca na način koji je kompatibilan sa prethodnim koracima

Ovo je najčešća situacija s kojom se možemo susresti. Moramo napraviti unazad nekompatibilne promjene. Već smo dokazali da za implementaciju bez zastoja ne bismo trebali jednostavno primijeniti migraciju baze podataka bez dodatnih koraka. U ovom dijelu članka, izvršit ćemo 3 implementacije aplikacije zajedno s migracijama baze podataka kako bismo postigli željeni rezultat uz održavanje kompatibilnosti unatrag.

Napomena. Podsjetimo da imamo bazu podataka verzija v1. Sadrži kolone first_name и last_name. Moramo se promijeniti last_name na surname. Imamo i verziju aplikacije 1.0.0, koji se još ne koristi surname.

Korak 2: Dodajte prezime

Verzija aplikacije: 2.0.0
DB verzija: v2

komentar

Dodavanjem nove kolone i kopiranjem njenog sadržaja, kreiramo unazad kompatibilne promjene baze podataka. U isto vreme, ako vratimo JAR ili pokrenemo stari JAR, on se neće pokvariti tokom izvršavanja.

Uvodimo novu verziju

Koraci:

  1. izvršite migraciju baze podataka da kreirate novu kolonu surname. Sada vaša DB verzija v2
  2. kopiraj podatke iz last_name в surname. Obrati pažnjuda ako imate puno ovih podataka, trebali biste razmisliti o batch migraciji!
  3. napišite kod gdje se koriste OBA и новыйi stari kolona. Sada vaša verzija aplikacije 2.0.0
  4. pročitajte vrijednost iz kolone surname, ako nije null, ili od last_name, ako a surname nije određeno. Možete izbrisati getLastName() iz koda, budući da će ispisati null prilikom vraćanja vaše aplikacije iz 3.0.0 do 2.0.0.

Ako koristite Spring Boot Flyway, ova dva koraka će se izvršiti tokom pokretanja verzije 2.0.0 aplikacije. Ako ručno pokrenete alat za određivanje verzija baze podataka, morat ćete učiniti dvije različite stvari da biste to učinili (prvo ručno ažurirajte verziju db-a, a zatim implementirajte novu aplikaciju).

Važno je. Zapamtite da je novokreirana kolona NE TREBA biti NOT NULL. Ako izvršite rollback, stara aplikacija ne zna za novu kolonu i neće je instalirati tokom Insert. Ali ako dodate ovo ograničenje i vaš db će biti v2, ovo će zahtijevati postavljanje vrijednosti nove kolone. Što će dovesti do kršenja ograničenja.

Važno je. Trebali biste ukloniti metodu getLastName(), jer u verziji 3.0.0 Ne postoji koncept kolone u kodu last_name. To znači da će tamo biti postavljena null. Možete ostaviti metodu i dodati provjere za null, ali puno bolje rješenje bi bilo da se uvjerimo da je to u logici getSurname() odabrali ste ispravnu vrijednost različitu od nule.

A/B testiranje

Trenutna situacija je da imamo verziju aplikacije 1.0.0, raspoređeno u proizvodnji, a baza podataka u v1. Moramo implementirati drugu instancu aplikacije verzije 2.0.0koji će ažurirati bazu podataka na v2.

Koraci:

  1. postavlja se nova instanca aplikacije verzije 2.0.0koji ažurira bazu podataka na v2
  2. u međuvremenu su neki zahtjevi obrađeni od strane instanci verzije 1.0.0
  3. ažuriranje je bilo uspješno i imate više pokrenutih instanci verzije aplikacije 1.0.0 i druge verzije 2.0.0. Svi komuniciraju sa bazom podataka v2
  4. verzija 1.0.0 ne koristi stupac prezimena u bazi, već verziju 2.0.0 koristi. Ne ometaju jedni druge i ne bi trebalo biti grešaka.
  5. verzija 2.0.0 pohranjuje podatke i u staru i u novu kolonu, osiguravajući kompatibilnost unatrag

Važno je. Ako imate bilo kakve upite koji broje stavke na osnovu vrijednosti iz stare/nove kolone, imajte na umu da sada imate duplirane vrijednosti (najvjerovatnije se još uvijek migriraju). Na primjer, ako želite da izbrojite broj korisnika čije prezime (kako god se kolona zove) počinje slovom A, zatim dok se migracija podataka ne završi (oldnew stupac) možda imate nedosljedne podatke ako postavljate upit za novu kolonu.

Vraćanje aplikacije

Sada imamo verziju aplikacije 2.0.0 i baza podataka u v2.

Koraci:

  1. vratite svoju aplikaciju na verziju 1.0.0.
  2. verzija 1.0.0 ne koristi stupac u bazi podataka surname, tako da bi vraćanje trebalo biti uspješno

DB promjene

Baza podataka sadrži stupac pod nazivom last_name.

Izvorna skripta Flywaya:

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

Dodaj skriptu surname.

Pažnja. Zapamtite da NE MOŽETE DODATI nikakva NOT NULL ograničenja u kolonu koju dodajete. Ako vratite JAR, stara verzija neće imati pojma o dodanoj koloni i automatski će je postaviti na NULL. Ako postoji takvo ograničenje, stara aplikacija će se jednostavno pokvariti.

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

Promjene koda

Podatke pohranjujemo kao u last_name, i surname. U isto vrijeme čitamo iz last_name, jer je ova kolona najrelevantnija. Tokom procesa implementacije, neki zahtjevi su možda obrađeni od strane instance aplikacije koja još nije ažurirana.

/*
 * 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
                + "]";
    }
}

Korak 3: Uklanjanje prezimena iz koda

Verzija aplikacije: 3.0.0

DB verzija:v3

komentar

Bilješka per.: Očigledno, u originalnom članku autor je greškom kopirao tekst ovog bloka iz koraka 2. U ovom koraku treba izvršiti promjene u kodu aplikacije s ciljem uklanjanja funkcionalnosti koja koristi kolonu last_name.

Dodavanjem nove kolone i kopiranjem njenog sadržaja, kreirali smo unazad kompatibilne promjene baze podataka. Takođe, ako vratimo JAR ili pokrenemo stari JAR, on se neće pokvariti tokom izvršavanja.

Vraćanje aplikacije

Trenutno imamo verziju aplikacije 3.0.0 i bazu podataka v3. Verzija 3.0.0 ne sprema podatke na last_name. To znači da u surname pohranjuju se najažurnije informacije.

Koraci:

  1. vratite svoju aplikaciju na verziju 2.0.0.
  2. verzija 2.0.0 koristi i last_name и surname.
  3. verzija 2.0.0 će uzeti surname, ako nije nula, inače -last_name

Promjene u bazi podataka

U bazi podataka nema strukturnih promjena. Sljedeća skripta se izvršava da izvrši konačnu migraciju starih podataka:

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

Promjene koda

Bilješka per.: Opis ovog bloka je takođe greškom kopirao autor iz koraka 2. U skladu sa logikom članka, promene u kodu u ovom koraku treba da imaju za cilj da iz njega uklone elemente koji rade sa kolonom last_name.

Podatke pohranjujemo kao last_name, i surname. Osim toga, čitamo iz kolumne last_name, jer je najrelevantniji. Tokom procesa implementacije, neke zahtjeve može obraditi instanca koja još nije nadograđena.

/*
 * 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
                + "]";
    }
}

Korak 4: Uklanjanje prezimena iz baze podataka

Verzija aplikacije: 4.0.0

DB verzija: v4

komentar

Zbog činjenice da je kod verzije 3.0.0 nije koristio kolonu last_name, ništa loše se neće dogoditi tokom izvršenja ako se vratimo na 3.0.0 nakon uklanjanja kolone iz baze podataka.

Dnevnici izvršavanja skripte

We will do it in the following way:

01) Run 1.0.0
02) Wait for the app (1.0.0) to boot
03) Generate a person by calling POST localhost:9991/person to version 1.0.0
04) Run 2.0.0
05) Wait for the app (2.0.0) to boot
06) Generate a person by calling POST localhost:9991/person to version 1.0.0
07) Generate a person by calling POST localhost:9992/person to version 2.0.0
08) Kill app (1.0.0)
09) Run 3.0.0
10) Wait for the app (3.0.0) to boot
11) Generate a person by calling POST localhost:9992/person to version 2.0.0
12) Generate a person by calling POST localhost:9993/person to version 3.0.0
13) Kill app (3.0.0)
14) Run 4.0.0
15) Wait for the app (4.0.0) to boot
16) Generate a person by calling POST localhost:9993/person to version 3.0.0
17) Generate a person by calling POST localhost:9994/person to version 4.0.0

Starting app in version 1.0.0
Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}

Starting app in version 2.0.0

Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}

Generate a person in version 2.0.0
Sending a post to 127.0.0.1:9992/person. This is the response:

{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}

Killing app 1.0.0

Starting app in version 3.0.0

Generate a person in version 2.0.0
Sending a post to 127.0.0.1:9992/person. This is the response:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}

Generate a person in version 3.0.0
Sending a post to 127.0.0.1:9993/person. This is the response:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}

Killing app 2.0.0

Starting app in version 4.0.0

Generate a person in version 3.0.0
Sending a post to 127.0.0.1:9993/person. This is the response:

{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}

Generate a person in version 4.0.0
Sending a post to 127.0.0.1:9994/person. This is the response:

{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}

DB promjene

Relativno v3 samo uklonimo kolonu last_name i dodajte ograničenja koja nedostaju.

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

Promjene koda

Nema promjena u kodu.

zaključak

Uspješno smo primijenili promjenu naziva kolone koja nije kompatibilna unatrag izvodeći nekoliko unatrag kompatibilnih implementacija. Ispod je sažetak izvršenih radnji:

  1. implementacija verzije aplikacije 1.0.0 с v1 shema baze podataka (ime kolone = last_name)
  2. implementacija verzije aplikacije 2.0.0, koji pohranjuje podatke u last_name и surname. Aplikacija čita iz last_name. Baza podataka je u verziji v2koji sadrže kolone poput last_name, i surname. surname je kopija last_name. (NAPOMENA: ova kolona ne smije imati ograničenje ne null)
  3. implementacija verzije aplikacije 3.0.0, koji samo pohranjuje podatke u surname i čita iz prezimena. Što se baze podataka tiče, poslednja migracija je u toku last_name в surname. Takođe ograničenje NOT NULL povučen iz last_name. Baza podataka je sada u verziji v3
  4. implementacija verzije aplikacije 4.0.0 - nema promjena u kodu. Postavljanje baze podataka v4, koji uklanja last_name. Ovdje možete dodati sva ograničenja koja nedostaju u bazu podataka.

Slijedeći ovaj pristup uvijek možete vratiti jednu verziju bez narušavanja kompatibilnosti baze podataka/aplikacija.

Kod

Sav kod korišten u ovom članku dostupan je na GitHub. Ispod je dodatni opis.

Projekti

Nakon kloniranja spremišta, vidjet ćete sljedeću strukturu foldera.

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

Skripte

Možete pokrenuti skripte opisane u skriptama ispod, koje će pokazati kompatibilne i nekompatibilne promjene u bazi podataka.

To see kućište sa izmjenama kompatibilnim s unazad, pokreni:

./scripts/scenario_backward_compatible.sh

I da vidim slučaj sa unatrag nekompatibilnim promjenama, pokreni:

./scripts/scenario_backward_incompatible.sh

Uzorak proljetnih čizama Flyway

Svi primjeri su preuzeti iz Spring Boot Sample Flyway.

Možete pogledati http://localhost:8080/flyway, postoji lista skripti.

Ovaj primjer također uključuje konzolu H2 (at http://localhost:8080/h2-console) tako da možete vidjeti status baze podataka (zadani jdbc URL je jdbc:h2:mem:testdb).

dodatno

Pročitajte i druge članke na našem blogu:

izvor: www.habr.com

Dodajte komentar