Implementacija i baze podataka bez prekida rada

Implementacija i baze podataka bez prekida rada

Ovaj članak detaljno objašnjava kako riješiti probleme s kompatibilnošću baze podataka u implementaciji. Reći ćemo vam što se može dogoditi vašim produkcijskim aplikacijama ako ih pokušate implementirati bez prethodne pripreme. Zatim ćemo proći kroz faze životnog ciklusa aplikacije koje su potrebne za nula vremena zastoja (cca. traka: dalje - nula zastoja). Rezultat naših operacija bit će primijeniti unatrag nekompatibilnu promjenu baze podataka na unatrag kompatibilan način.

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

Uvod

Uvođenje bez zastoja

Kakva mistika nulta implementacija zastoja? Možete reći da je to kada je vaša aplikacija postavljena na takav način da možete uspješno uvesti novu verziju aplikacije u proizvodnju, a da korisnik ne primijeti njezinu nedostupnost. Iz perspektive korisnika i tvrtke, ovo je najbolji mogući scenarij implementacije jer omogućuje uvođenje novih značajki i ispravljanje grešaka bez prekida.

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

  • implementirajte verziju br. 1 svoje usluge
  • izvršiti migraciju baze podataka
  • Implementirajte verziju #2 svoje usluge paralelno s verzijom #1
  • čim vidite da verzija br. 2 radi kako treba, uklonite verziju br. 1
  • spremni!

Lako, zar ne? Nažalost, to nije tako jednostavno i to ćemo detaljnije pogledati kasnije. Sada provjerimo još jedan prilično uobičajeni proces implementacije - plavozelenu implementaciju.

Jeste li ikada čuli plavo zelena implementacija? Cloud Foundry ovo čini iznimno lakim. Samo pogledajte ovaj članak, gdje to detaljnije opisujemo. Da ukratko rezimiramo, podsjetimo vas kako napraviti plavozelenu implementaciju:

  • osigurati da dvije kopije vašeg proizvodnog koda ("plava" i "zelena") rade;
  • sav promet usmjeriti u plavo okruženje, tj. tako da proizvodni URL-ovi upućuju tamo;
  • implementirati i testirati sve promjene aplikacije u zelenom okruženju;
  • prebacite URL-ove iz plavog u zeleno okruženje

Plavo zelena implementacija pristup je koji vam omogućuje jednostavno uvođenje novih značajki bez brige o prekidu proizvodnje. To je zbog činjenice da čak i ako se nešto dogodi, možete se jednostavno vratiti na prethodno okruženje jednostavnim "pritiskom prekidača".

Nakon što pročitate sve gore navedeno, možda ćete se zapitati: kakve veze ima nulti prekid rada s plavozelenom implementacijom?

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

Druga je mogućnost korištenje iste baze podataka, stvaranje plavo-zelenih prekidača za web i slojeve domene. U ovom pristupu, baza podataka često može predstavljati problem, osobito kada trebate promijeniti njezinu shemu kako bi podržala novu verziju softvera.

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

izvršiti migraciju baze podataka.

Sada si morate postaviti pitanje - što ako promjena baze podataka nije unatrag kompatibilna? Neće li se moja prva verzija aplikacije pokvariti? Zapravo, upravo će se to dogoditi...

Dakle, čak i unatoč ogromnim prednostima nultog vremena zastoja / plavozelene implementacije, tvrtke imaju tendenciju slijediti sljedeći sigurniji postupak za implementaciju svojih aplikacija:

  • pripremiti paket s novom verzijom aplikacije
  • zatvorite pokrenutu aplikaciju
  • pokrenuti skripte za migraciju baze podataka
  • implementirati i pokrenuti novu verziju aplikacije

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

Problemi s bazom podataka

Ako imate aplikaciju bez statusa koja ne pohranjuje nikakve podatke u bazu podataka, možete odmah dobiti nultu implementaciju zastoja. Nažalost, većina softvera treba negdje pohraniti podatke. Zbog toga biste trebali dobro razmisliti prije bilo kakvih promjena na strujnom krugu. Prije nego što uđemo u detalje o tome kako promijeniti shemu tako da je moguća implementacija bez zastoja, prvo se usredotočimo na shemu upravljanja verzijama.

Shema određivanja verzija

U ovom ćemo članku koristiti Flyway kao alat za kontrolu verzija (cca. Prijevod: govorimo o migracijama baza podataka). Naravno, također ćemo napisati Spring Boot aplikaciju koja ima ugrađenu podršku za Flyway i izvršit će migraciju sheme dok postavljamo kontekst aplikacije. Kada koristite Flyway, možete pohraniti migracijske skripte u svoju mapu projekata (prema zadanim postavkama 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 migracijske skripte koje će se, ako se prethodno ne izvrše, izvršiti jedna za drugom kada se aplikacija pokrene. Pogledajmo jednu od datoteka (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 samoobjašnjivo: možete koristiti SQL da definirate kako se vaša baza podataka treba mijenjati. Za više informacija o Spring Boot i Flyway, pogledajte Spring Boot Docs.

Korištenjem alata za kontrolu izvora uz Spring Boot dobivate 2 velike prednosti:

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

Rješavanje problema s bazom podataka

U sljedećem odjeljku članka usredotočit ćemo se na dva pristupa promjenama baze podataka.

  • unatrag nekompatibilnost
  • kompatibilnost unatrag

Prvi će se smatrati upozorenjem da ne biste trebali provoditi implementaciju bez prekida rada bez prethodne pripreme... Drugi nudi rješenje kako možete izvršiti implementaciju bez prekida rada i istovremeno zadržati kompatibilnost sa prethodnim verzijama.

Naš projekt 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 stol 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.

Bilješka. Poslovni PRO-SAVJET. Pojednostavljenje procesa može vam uštedjeti mnogo novca na podršci (što više ljudi radi u vašoj tvrtki, to više novca možete uštedjeti)!

Nema potrebe za vraćanjem baze podataka

To pojednostavljuje proces implementacije (neke su povratne verzije baze podataka gotovo nemoguće, kao što je vraćanje nakon brisanja). Radije vraćamo samo aplikacije. Na ovaj način, čak i ako imate različite baze podataka (na primjer, SQL i NoSQL), vaš cjevovod implementacije izgledat će isto.

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

Vraćanje unatrag treba učiniti samo kada je potrebno. Ako u trenutnoj verziji postoji greška koju nije lako popraviti, trebali bismo se moći vratiti na posljednju radnu verziju. Pretpostavljamo da je ova zadnja radna verzija prethodna. Održavanje kompatibilnosti koda i baze podataka za više od jednog uvođenja bilo bi izuzetno teško i skupo.

Bilješka. Radi bolje čitljivosti, u ovom ćemo članku promijeniti glavnu verziju aplikacije.

Korak 1: Početno stanje

Verzija aplikacije: 1.0.0
DB verzija: v1

Komentirati

Ovo će biti početno stanje aplikacije.

Promjene baze 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 osobama 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
                + "]";
    }
}

Unatrag nekompatibilno preimenovanje stupaca

Pogledajmo primjer kako promijeniti naziv stupca:

Pažnja. Sljedeći primjer će namjerno pokvariti stvari. Prikazujemo ovo kako bismo demonstrirali problem kompatibilnosti baze podataka.

Verzija aplikacije: 2.0.0.BAD

DB verzija: v2bad

Komentirati

Trenutne promjene NE dopuštaju nam pokretanje dvije instance (stare i nove) u isto vrijeme. Stoga će biti teško postići nultu implementaciju zastoja (ako se uzmu u obzir pretpostavke, to je zapravo nemoguće).

A/B testiranje

Trenutna situacija je takva da imamo verziju aplikacije 1.0.0, raspoređen 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 bilo je uspješno i neke su instance pokrenute 1.0.0, ostali - u 2.0.0.BAD. Sve je povezano s bazom podataka v2bad
  4. sve instance verzije 1.0.0 počet će izbacivati ​​pogreške jer će pokušati umetnuti podatke u stupac last_namekoji više ne postoji
  5. sve instance verzije 2.0.0.BAD radit će bez problema

Kao što vidite, ako napravimo unatrag 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 vjerojatno ovdje mislio na A/B testiranje) odlučili smo da moramo vratiti aplikaciju na verziju 1.0.0. Recimo da ne želimo vratiti bazu podataka.

Koraci:

  1. zaustavljamo instancu aplikacije verzije 2.0.0.BAD
  2. baza podataka je i dalje v2bad
  3. budući da je verzija 1.0.0 ne razumije što je to surname, vidjet ćemo pogreške
  4. pakao je nastao, ne možemo se više vratiti

Kao što vidite, ako napravimo unatrag 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 baze podataka

Skripta za migraciju koja preimenuje last_name в surname

Izvorna Flyway skripta:

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.

Promjena naziva stupca na način koji je kompatibilan sa prethodnim verzijama

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

Bilješka. Podsjetimo se da imamo bazu podataka verzija v1. Sadrži stupce 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

Komentirati

Dodavanjem novog stupca i kopiranjem njegovog sadržaja stvaramo unatrag kompatibilne promjene baze podataka. U isto vrijeme, ako vratimo JAR ili pokrenemo stari JAR, neće se pokvariti tijekom izvođenja.

Izbacujemo novu verziju

Koraci:

  1. izvršiti migraciju baze podataka za stvaranje novog stupca surname. Sada vaša DB verzija v2
  2. kopirati podatke iz last_name в surname. Imajte na umuda ako imate puno ovih podataka, trebali biste razmisliti o serijskoj migraciji!
  3. napišite kod gdje se koriste OBA и noviI stari stupac. Sada vaša verzija aplikacije 2.0.0
  4. očitajte vrijednost iz stupca surname, ako nije null, odnosno od last_name, ako surname nije specificirano. Možete izbrisati getLastName() iz koda, budući da će izlaz null kada vraćate svoju aplikaciju iz 3.0.0 na 2.0.0.

Ako koristite Spring Boot Flyway, ova dva koraka će se izvesti tijekom pokretanja verzije 2.0.0 aplikacije. Ako ručno pokrenete alat za izradu verzija baze podataka, morat ćete učiniti dvije različite stvari da to učinite (prvo ručno ažurirajte verziju baze podataka, a zatim postavite novu aplikaciju).

Važno je. Zapamtite da je novostvoreni stupac NE SMIJE biti NIJE NULJA. Ako napravite vraćanje, stara aplikacija ne zna za novi stupac i neće ga instalirati tijekom Insert. Ali ako dodate ovo ograničenje i vaš db će biti v2, to će zahtijevati postavljanje vrijednosti novog stupca. Što će dovesti do kršenja ograničenja.

Važno je. Trebali biste ukloniti metodu getLastName(), jer u verziji 3.0.0 U kodu ne postoji koncept stupca last_name. To znači da će null biti postavljen tamo. Možete ostaviti metodu i dodati provjere za null, ali puno bolje rješenje bilo bi to provjeriti u logici getSurname() odabrali ste ispravnu vrijednost koja nije nula.

A/B testiranje

Trenutna situacija je takva da imamo verziju aplikacije 1.0.0, raspoređen 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 neke zahtjeve obradile instance verzije 1.0.0
  3. ažuriranje je bilo uspješno i imate više pokrenutih instanci aplikacije verzije 1.0.0 i druge verzije 2.0.0. Svatko komunicira s bazom podataka u v2
  4. verzija 1.0.0 ne koristi stupac prezimena u bazi, već verziju 2.0.0 koristi. Oni ne smetaju jedni drugima i ne bi trebalo biti grešaka.
  5. verzija 2.0.0 pohranjuje podatke u starom i novom stupcu, osiguravajući kompatibilnost s prethodnim verzijama

Važno je. Ako imate bilo kakve upite koji broje stavke na temelju vrijednosti iz starog/novog stupca, trebali biste zapamtiti da sada imate duplicirane vrijednosti (najvjerojatnije još uvijek migriraju). Na primjer, ako želite izbrojati broj korisnika čije prezime (kako god se stupac zove) počinje slovom A, zatim dok se ne završi migracija podataka (oldnew stupac) možete imati nedosljedne podatke ako postavite upit novom stupcu.

Vraćanje aplikacije

Sada imamo verziju aplikacije 2.0.0 i bazu 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.

Flyway izvorna skripta:

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. Imajte na umu da NE MOŽETE DODATI nikakva NOT NULL ograničenja u stupac koji dodajete. Ako vratite JAR, stara verzija neće imati pojma o dodanom stupcu i automatski će ga 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 last_name, i u surname. Istovremeno čitamo iz last_name, budući da je ovaj stupac najrelevantniji. Tijekom procesa implementacije, neke je zahtjeve možda obradila instanca 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

Komentirati

Bilješka per.: Očigledno je u izvornom članku autor greškom kopirao tekst ovog bloka iz koraka 2. U ovom koraku treba napraviti izmjene u kodu aplikacije s ciljem uklanjanja funkcionalnosti koja koristi stupac last_name.

Dodavanjem novog stupca i kopiranjem njegovog sadržaja stvorili smo unatrag kompatibilne promjene baze podataka. Također, ako vratimo JAR na staro stanje ili pokrenemo stari JAR, on se neće pokvariti tijekom izvođenja.

Vraćanje aplikacije

Trenutno imamo verziju aplikacije 3.0.0 i baze podataka v3. Verzija 3.0.0 ne sprema podatke na last_name. To znači da u surname pohranjuju se najsvježije 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 potrajati surname, ako nije nula, inače -last_name

Promjene baze podataka

U bazi podataka nema strukturnih promjena. Sljedeća skripta se izvršava za izvođenje konačne migracije 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.: Autor je također greškom kopirao opis ovog bloka iz koraka 2. U skladu s logikom članka, promjene u kodu u ovom koraku trebale bi biti usmjerene na uklanjanje iz njega elemenata koji rade sa stupcem last_name.

Podatke pohranjujemo kao last_name, i u surname. Dodatno, čitamo iz kolumne last_name, budući da je najrelevantniji. Tijekom 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

Komentirati

Zbog činjenice da kod verzije 3.0.0 nije koristio stupac last_name, neće se dogoditi ništa loše tijekom izvođenja ako se vratimo na 3.0.0 nakon uklanjanja stupca 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

oko v3 samo uklanjamo stupac last_name i dodajte nedostajuća ograničenja.

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

Izlaz

Uspješno smo primijenili unatrag nekompatibilnu promjenu naziva stupca izvođenjem nekoliko unatrag kompatibilnih implementacija. U nastavku je sažetak izvršenih radnji:

  1. implementacija verzije aplikacije 1.0.0 с v1 shema baze podataka (naziv stupca = last_name)
  2. implementacija verzije aplikacije 2.0.0, koji pohranjuje podatke u last_name и surname. Aplikacija glasi iz last_name. Baza podataka je u verziji v2koji sadrži stupce poput last_nameI surname. surname je kopija last_name. (NAPOMENA: Ovaj stupac ne smije imati ograničenje not null)
  3. implementacija verzije aplikacije 3.0.0, koji samo pohranjuje podatke u surname a glasi od prezimena. Što se tiče baze podataka, u tijeku je zadnja migracija last_name в surname. Također ograničenje NIJE NULJA uklonjeno iz last_name. Baza podataka je sada u verziji v3
  4. implementacija verzije aplikacije 4.0.0 - nema izmjena koda. Implementacija 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 prekidanja kompatibilnosti baze podataka/aplikacije.

Šifra

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

Projekti

Nakon kloniranja repozitorija, vidjet ćete sljedeću strukturu mapa.

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

skripta

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

Vidjeti slučaj s unatrag kompatibilnim promjenama, trčanje:

./scripts/scenario_backward_compatible.sh

I vidjeti slučaj s unatrag nekompatibilnim promjenama, trčanje:

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Svi primjeri preuzeti su iz Spring Boot Sample Flyway.

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

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

dodatno

Također pročitajte ostale članke na našem blogu:

Izvor: www.habr.com

Dodajte komentar