Nulles dīkstāves izvietojums un datu bāzes

Nulles dīkstāves izvietojums un datu bāzes

Å ajā rakstā detalizēti skaidrots, kā risināt problēmas, kas saistÄ«tas ar datu bāzu saderÄ«bu, veicot izvietoÅ”anu. Mēs pastāstÄ«sim, kas var notikt ar jÅ«su ražoÅ”anas lietojumprogrammām, ja mēģināsiet veikt izvietoÅ”anu bez iepriekŔējas sagatavoÅ”anas. Pēc tam mēs iziesim cauri lietojumprogrammas dzÄ«ves cikla posmiem, kas ir nepiecieÅ”ami, lai nodroÅ”inātu nulles dÄ«kstāvi (piezÄ«me: turpmāk – nulles dÄ«kstāve). MÅ«su darbÄ«bu rezultāts bÅ«s atpakaļsaderÄ«gu datu bāzes izmaiņu piemēroÅ”ana.

Ja vēlaties iepazīties ar rakstā iekļautajiem kodu piemēriem, jūs tos atradīsiet GitHub.

Ievads

Nulles dīkstāves izvietoŔana

Kas ir Å”is noslēpumainais nulles dÄ«kstāves izvietojums? Можно ŃŠŗŠ°Š·Š°Ń‚ŃŒ, ŃŃ‚Š¾ когГа ваше приложение Ń€Š°Š·Š²ŠµŃ€Š½ŃƒŃ‚Š¾ так, что вы можете успешно Š²Š²Š¾Š“ŠøŃ‚ŃŒ Š½Š¾Š²ŃƒŃŽ Š²ŠµŃ€ŃŠøŃŽ ŠæŃ€ŠøŠ»Š¾Š¶ŠµŠ½ŠøŃ на ŠæŃ€Š¾Š“Š°ŠŗŃˆŠ½, в то Š²Ń€ŠµŠ¼Ń как ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»ŃŒ не замечает его Š½ŠµŠ“Š¾ŃŃ‚ŃƒŠæŠ½Š¾ŃŃ‚Šø. Š” точки Š·Ń€ŠµŠ½ŠøŃ ŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŠµŠ»Ń Šø компании, ŃŃ‚Š¾ Š½Š°ŠøŠ»ŃƒŃ‡ŃˆŠøŠ¹ ŠøŠ· возможных сценариев Š“ŠµŠæŠ»Š¾Ń, поскольку таким образом можно Š²Š²Š¾Š“ŠøŃ‚ŃŒ новые Ń„ŃƒŠ½ŠŗŃ†ŠøŠø Šø ŃƒŃŃ‚Ń€Š°Š½ŃŃ‚ŃŒ ошибки без перебоев в работе.

Kā to sasniegt? Ir vairāki veidi; Ŕis ir viens no tiem:

  • izvietojiet savu pakalpojuma versiju Nr. 1
  • veiciet datu bāzes migrāciju
  • izvietojiet pakalpojuma versiju Nr. 2 paralēli versijai Nr. 1
  • uzreiz, kad redzat, ka versija Nr. 2 darbojas pareizi, noņemiet versiju Nr. 1
  • gatavs!

Viegls, vai ne? Diemžēl tas nav tik vienkārÅ”i, un mēs vēlāk par to detalizēti diskutēsim. Tagad paskatÄ«simies uz vēl vienu diezgan izplatÄ«tu izvietoÅ”anas procesu — zibens zaļo izvietoÅ”anu.

Vai esat dzirdējuÅ”i par zibens zaļo izvietoÅ”anu? Š” Cloud Foundry ŃŃ‚Š¾ чрезвычайно легко ŃŠ“ŠµŠ»Š°Ń‚ŃŒ. ŠŸŃ€Š¾ŃŃ‚Š¾ Š³Š»ŃŠ½ŃŒŃ‚Šµ на Å”ai rakstam, kur mēs to aprakstām sÄ«kāk. ÄŖsi sakot, atgādināsim, kā veikt zibens zaļo izvietoÅ”anu:

  • nodroÅ”iniet, lai divas jÅ«su ražoÅ”anas koda kopijas (ā€œzilaā€ un ā€œzaļaā€) darbotos;
  • virziet visu trafiku uz zilo vidi, t.i., lai ražoÅ”anas URL norādÄ«tu uz to;
  • izvietojiet un pārbaudiet visus lietojumprogrammas izmaiņu zaļajā vidē;
  • pārslēdziet URL adreses no zilās uz zaļo vidi

Zibens zaļā izvietoÅ”ana ir pieeja, kas ļauj jums viegli ieviest jaunas funkcijas, neriskējot, ka ražoÅ”anas vide sabojāsies. Tas saistÄ«ts ar to, ka pat ja kaut kas notiek, jÅ«s varat viegli atgriezties iepriekŔējā vidē, vienkārÅ”i ā€žpārslēdzot slēdziā€.

Izlasot iepriekÅ” minēto, jÅ«s varētu uzdot jautājumu: kāda saistÄ«ba ir nulles dÄ«kstāvei ar zibens zaļo izvietoÅ”anu?

Nu, tām ir diezgan daudz kopÄ«ga, jo divu vienas un tās paÅ”as vides kopiju uzturēŔana prasa dubultus centienus to apkalpoÅ”anai. Tādēļ daži komandas, kā apgalvo Martins Fowlers, ievēro Ŕī pieejas variāciju:

Otra iespēja ir izmantot to paÅ”u datu bāzi, izveidojot zilzaļās slēdžus tÄ«mekļa un domēna slāņiem. Šādā pieejā datu bāzes bieži var kļūt par problēmu, Ä«paÅ”i, ja jums ir jāpārveido tās shēma, lai atbalstÄ«tu jauno programmatÅ«ras versiju.

Un Å”eit mēs nonākam pie galvenās problēmas Å”ajā rakstā. Datu bāze. ApskatÄ«sim Å”o frāzi vēlreiz.

veiciet datu bāzes migrāciju.

Tagad jums jāuzdod sev jautājums - kas notiks, ja datu bāzes izmaiņas bÅ«s neapmierinoÅ”as? Vai mana pirmā lietotnes versija nesabruks? PatiesÄ«bā, tieÅ”i tas notiks...

Tādējādi, neraugoties uz milzÄ«gajām priekÅ”rocÄ«bām, kas saistÄ«tas ar zero downtime / blue green deployment, uzņēmumi mēdz ievērot Ŕādu droŔāku lietotņu izvietoÅ”anas procesu:

  • sagatavot pakotni ar jauno lietotnes versiju
  • izslēgt palaisto lietotni
  • izpildÄ«t skriptus datu bāzes migrācijai
  • izvietot un palaist jauno lietotnes versiju

Šajā rakstā mēs detalizēti aprakstīsim, kā jūs varat strādāt ar datu bāzi un kodu, lai gūtu labumu no zero downtime deployment.

Problēmas ar datu bāzi

Ja jums ir stateless lietotne, kas neglabā nekādus datus datu bāzē, jūs varat iegūt zero downtime deployment uzreiz. Diemžēl lielākā daļa programmatūras ir jāuzglabā kaut kur. Tāpēc jums ir divreiz jāpārdomā, pirms veicat jebkādas izmaiņas shēmā. Pirms mēs iegrimsim detaļās par to, kā mainīt shēmu tā, lai būtu iespējams izvietot bez dīkstāves, vispirms koncentrēsimies uz versiju vadības shēmu.

Versiju vadības shēma

Å ajā rakstā mēs izmantosim Flyway kā versiju vadÄ«bas rÄ«ku (piezÄ«me: runa ir par datu bāzes migrācijām). Protams, mēs arÄ« uzrakstÄ«sim Spring Boot lietotni, kurai ir iebÅ«vēta Flyway atbalsts un kas veiks shēmas migrāciju lietotnes konteksta iestatīŔanas laikā. Izmantojot Flyway, jÅ«s varat saglabāt migrācijas skriptus savus projektu mapēs (pēc noklusējuma classpath:db/migration). Å eit jÅ«s varat redzēt Ŕādu migrācijas failu piemēru

└── db
 └── migration
 ā”œā”€ā”€ V1__init.sql
 ā”œā”€ā”€ V2__Add_surname.sql
 ā”œā”€ā”€ V3__Final_migration.sql
 └── V4__Remove_lastname.sql

Å ajā piemērā mēs redzam 4 migrācijas scenārijus, kas, ja tie iepriekÅ” nebija veikti, tiks izpildÄ«ti viens pēc otra, palaižot lietojumprogrammu. ApskatÄ«sim vienu no failiem (V1__init.sql) kā piemēru.

IZVEIDOT TABULU PERSON (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
fist_name varchar(255) not null,
last_name varchar(255) not null
);

ielikt iekŔā PERSON (first_name, last_name) vērtÄ«bas ('Dave', 'Syer');

Viss skaisti runā pats par sevi: jūs varat izmantot SQL, lai noteiktu, kā jūsu datubāze būtu jāmaina. Lai iegūtu papildu informāciju par Spring Boot un Flyway, iepazīstieties ar Spring Boot dokumentāciju.

Izmantojot versiju pārvaldÄ«bas rÄ«ku kopā ar Spring Boot, jÅ«s saņemat 2 lielas priekÅ”rocÄ«bas:

  • jÅ«s atdalāt datubāzes izmaiņas no koda izmaiņām
  • datubāzes migrācija notiek vienlaikus ar jÅ«su lietojumprogrammas laiÅ”anu tirgÅ«, tas ir, jÅ«su izvietoÅ”anas process tiek atvieglots

Problēmu risināŔana saistÄ«bā ar datubāzi

NākoÅ”ajā sadaļā mēs koncentrēsimies uz divu pieeju pārskatu saistÄ«bā ar datubāzes izmaiņām.

  • atpakaļsaderÄ«ba
  • atpakaļsaderÄ«bas

Pirmais tiks apskatÄ«ts kā brÄ«dinājums, ka nevajadzētu veikt bezdarbÄ«bas izvietojumus bez iepriekŔējas sagatavoÅ”anās… Otrais piedāvā risinājumu, kā var veikt izvietoÅ”anu bez pārtraukumiem un vienlaikus saglabāt atpakaļsaderÄ«bu.

MÅ«su projekts, ar kuru mēs strādāsim, bÅ«s vienkārÅ”a Spring Boot Flyway lietojumprogramma, kurā ir Person ar first_name un last_name datubāzē (piezÄ«me: Person ir tabula, bet first_name un last_name — tas ir lauks tajā). Mēs vēlamies pārdēvēt last_name par uzvārdu.

Pieņēmumi

Pirms mēs iedziļināmies detaļās, nepiecieÅ”ams izvirzÄ«t pāris pieņēmumus attiecÄ«bā uz mÅ«su lietojumprogrammām. Galvenais rezultāts, ko mēs vēlamies sasniegt, bÅ«s diezgan vienkārÅ”s process.

PiezÄ«me. Business PRO-TIP. Procesu vienkārÅ”oÅ”ana var ietaupÄ«t jums daudz naudas atbalstam (jo vairāk cilvēku strādā jÅ«su uzņēmumā, jo vairāk naudas jÅ«s varat ietaupÄ«t)!

Nav nepiecieŔams veikt datubāzes atgrieŔanu

Tas atvieglo izvietoÅ”anas procesu (dažas datubāzes atgrieÅ”anas ir praktiski neiespējamas, piemēram, dzēŔanas atgrieÅ”ana). Mēs dodam priekÅ”roku atgriezt tikai lietojumprogrammas. Tādējādi, pat ja jums ir dažādas datubāzes (piemēram, SQL un NoSQL), jÅ«su izvietoÅ”anas lÄ«nija izskatÄ«sies vienādi.

Ir jānodroÅ”ina, lai vienmēr bÅ«tu iespēja atgriezt lietojumprogrammu par vienu versiju atpakaļ (ne vairāk)

AtgrieÅ”ana jāveic tikai nepiecieÅ”amÄ«bas gadÄ«jumā. Ja paÅ”reizējā versijā ir kļūda, kuru nav viegli novērst, mums jābÅ«t spējÄ«giem atgriezties pie pēdējās darba versijas. Mēs pieņemam, ka Ŕī pēdējā darba versija ir iepriekŔējā. AtbalstÄ«t saderÄ«bu ar kodu un datu bāzi vairāk nekā vienai izlaidumam bÅ«tu ārkārtÄ«gi grÅ«ti un dārgi.

PiezÄ«me. Lai uzlabotu lasāmÄ«bu, Å”ajā rakstā mēs mainÄ«sim lietojumprogrammas galveno versiju.

1. solis: sākotnējais stāvoklis

Lietojumprogrammas versija: 1.0.0
Datu bāzes versija: v1

Komentārs

Tas būs lietojumprogrammas sākotnējais stāvoklis.

Datu bāzes izmaiņas

Datu bāze satur 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');

Koda izmaiņas

Lietojumprogramma saglabā datus Person 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
                + "]";
    }
}

Atpakaļ nesavienojama kolonnas pārdēvēŔana

Apskatīsim piemēru, kā mainīt kolonnas nosaukumu:

Uzmanību. Nākamais piemērs apzināti izraisa kļūmi. Mēs to rādām, lai demonstrētu datu bāzes saderības problēmu.

Lietojumprogrammas versija: 2.0.0.BAD

Datu bāzes versija: v2bad

Komentārs

PaÅ”reizējās izmaiņas NEļauj mums palaist divus instancus (veco un jauno) vienlaikus. Tādējādi bezpārtraukta izvietoÅ”ana bÅ«s grÅ«ti sasniedzama (ņemot vērā pieņēmumus, tas patiesÄ«bā ir neiespējami).

A/B testēŔana

PaÅ”reizējā situācija ir tāda, ka mums ir lietojumprogramma versijā 1.0.0, izvietota ražoÅ”anas vidē, un datu bāze v1. Mums jāizvieto otrais lietojumprogrammas instants, versijā 2.0.0.BAD, un jāatjaunina datu bāze lÄ«dz v2bad.

Soli pa solim:

  1. ir izvietots jaunais lietojumprogrammas instants versijā 2.0.0.BAD, kas atjaunina datu bāzi līdz v2bad
  2. datu bāzē v2bad kolonna last_name vairs neeksistē — to ir mainÄ«jusi uzvārdu
  3. datu bāzes un lietojumprogrammas atjauninājums ir veiksmÄ«gi pabeigts, un daži instanci darbojas 1.0.0, citi — 2.0.0.BAD. Visi ir saistÄ«ti ar datu bāzi v2bad
  4. visi instanci versijā 1.0.0 sāks izsist kļūdas, jo tie centīsies ievietot datus kolonnā last_name, kuru vairs nav
  5. visi instanci versijā 2.0.0.BAD darbojas bez problēmām

Kā redzat, ja mēs veicam atpakaļ nesavienojamas datu bāzes un lietojumprogrammas izmaiņas, A/B testēŔana nav iespējama.

Lietojumprogrammas atgrieŔana

Pieņemsim, ka pēc mēģinājuma veikt A/B izvietoÅ”anu (piezÄ«me: iespējams, autors Å”eit domāja A/B testēŔanu) mēs nolemjam, ka mums jāpārgatavojas lietojumprogrammai uz versiju 1.0.0. Pieņemsim, ka mēs nevēlamies veikt datu bāzes atgrieÅ”anu.

Soli pa solim:

  1. mēs apturām programmas instanci versijā 2.0.0.BAD
  2. datubāze joprojām v2bad
  3. jo versija 1.0.0 neizprot, kas ir uzvārdu, mēs redzēsim kļūdas
  4. ellē izlauzās brīvībā, mēs vairs nevaram atgriezties

Kā redzat, ja veicam atpakaļ nesaderÄ«gas izmaiņas BŠ” un programmā, mēs nevaram atgriezties pie iepriekŔējās versijas.

Izpildes skriptu žurnāli

Atpakaļ nesaderīgs scenārijs:

01) Izpildiet 1.0.0
02) Gaidiet, līdz programma (1.0.0) ielādējas
03) Radiet personu, zvanot POST localhost:9991/person versijai 1.0.0
04) Izpildiet 2.0.0.BAD
05) Gaidiet, līdz programma (2.0.0.BAD) ielādējas
06) Radiet personu, zvanot POST localhost:9991/person versijai 1.0.0 <-- tam jāneizdodas
07) Radiet personu, zvanot POST localhost:9992/person versijai 2.0.0.BAD <-- tam jāizdodas

Sākot programmu versijā 1.0.0
RadīŔana personai versijā 1.0.0
Sūtot pieprasījumu uz 127.0.0.1:9991/person. Tas ir atbildes rezultāts:

{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}

Sākot programmu versijā 2.0.0.BAD
RadīŔana personai versijā 1.0.0
Sūtot pieprasījumu uz 127.0.0.1:9991/person. Tas ir atbildes rezultāts:

curl: (22) PieprasÄ«tā URL atgrieza kļūdu: 500 IekŔējā servera kļūda

RadīŔana personai versijā 2.0.0.BAD
Sūtot pieprasījumu uz 127.0.0.1:9995/person. Tas ir atbildes rezultāts:

{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}

Datu bāzes izmaiņas

Migrācijas skripts, kas pārdēvē last_name par uzvārdu

Oriģinālais Flyway skripts:

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

Skripts, kas pārdēvē last_name.

-- Å Ä« izmaiņa ir atpakaļ nesaderÄ«ga - jums nav iespējams veikt A/B testēŔanu
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;

Koda izmaiņas

Mēs mainījām lauka nosaukumu lastName uz uzvārdu.

Kolonnas pārdēvēŔana atpakaļ saderÄ«gā veidā

Tā ir visbiežāk sastopamā situācija, ar kuru mēs varam saskarties. Mums jāveic atpakaļ nesaderÄ«gas izmaiņas. Mēs jau esam pierādÄ«juÅ”i, ka, lai izvairÄ«tos no dÄ«kstāves, mums nav jāpiemēro datubāzes migrācijas bez papildu darbÄ«bām. Å ajā raksta sadaļā mēs veiksim 3 programmatÅ«ras izvietoÅ”anas kopā ar datubāzu migrācijām, lai sasniegtu vēlamo rezultātu, saglabājot atpakaļ saderÄ«bu.

PiezÄ«me. Atgādinām, ka mums ir DŠ‘ versija v1. Tā satur kolonnas first_name un last_name. Mums jāmaina last_name uz uzvārdu. Mums arÄ« ir programma versijā 1.0.0, , kas pagaidām neizmanto uzvārdu.

2. solis: Pievienojam surname

Lietojumprogrammas versija: 2.0.0
Datu bāzes versija: v2

Komentārs

Pievienojot jaunu kolonnu un kopējot tās saturu, mēs izveidojam atpakaļ saderÄ«gas DŠ‘ izmaiņas. Tajā paŔā laikā, ja mēs atgriezÄ«sim JAR vai mums bÅ«s strādājoÅ”s vecais JAR, tas nepiesprāgs izpildes laikā.

Izlaižam jauno versiju

Soli pa solim:

  1. veiciet DŠ‘ migrāciju, lai izveidotu jauno kolonnu uzvārdu. Tagad jÅ«su DŠ‘ versija v2
  2. nokopējiet datus no last_name par uzvārdu. LÅ«dzu, ņemiet vērā,, ja jums ir daudz Å”o datu, jums jāpārdomā partiju migrācija!
  3. uzrakstiet kodu, kur tiek izmantots ABI un jauns, un vecais kolonna. Tagad jūsu lietojumprogrammas versija 2.0.0
  4. izlasiet vērtību no kolonnas uzvārdu, ja tā nav null, vai no last_name, ja uzvārdu nav norādīts. Jūs varat dzēst getLastName() no koda, jo tas atgriezīs null atgriežot jūsu lietojumprogrammu ar 3.0.0 līdz 2.0.0.

Ja jÅ«s izmantojat Spring Boot Flyway, Å”ie divi soļi tiks izpildÄ«ti versijas sākumā 2.0.0 lietojumprogrammas. Ja jÅ«s manuāli palaižat datu bāzes versiju vadÄ«bas rÄ«ku, jums bÅ«s jāveic divi atseviŔķi soļi (vispirms manuāli atjauniniet db versiju un pēc tam izvietojiet jaunu lietojumprogrammu).

SvarÄ«gi. Atcerieties, ka jaunizveidotajai kolonnai NEDRÄŖKST bÅ«t NOT NULL. Ja jÅ«s veicat atgrieÅ”anos, vecā lietojumprogramma nezinās par jauno kolonnu un neiestatÄ«s to laikā Insert. Bet, ja jÅ«s pievienosiet Å”o ierobežojumu, un jÅ«su DB bÅ«s v2, tas prasÄ«s jauno kolonnas vērtÄ«bu iestatīŔanu. Tas radÄ«s ierobežojumu pārkāpumus.

SvarÄ«gi. Jums vajadzētu izdzēst metodi getLastName(), jo versijā 3.0.0 koda konceptā nav kolonnas last_name. Tas nozÄ«mē, ka tur tiks iestatÄ«ti null. JÅ«s varat saglabāt metodi un pievienot pārbaudes uz null, bet daudz labāks risinājums bÅ«tu pārliecināties, ka loÄ£ikā getSurname() jÅ«s esat izvēlējuÅ”ies pareizo nenobÄ«doÅ”o vērtÄ«bu.

A/B testēŔana

PaÅ”reizējā situācija ir tāda, ka mums ir lietojumprogramma versijā 1.0.0, izvietota uz produkcijas, un DB ir v1. Mums jāizvieto otra versijas lietojumprogrammas instance 2.0.0, kas atjaunos datu bāzi lÄ«dz v2.

Soli pa solim:

  1. ir izvietots jaunais lietojumprogrammas instants versijā 2.0.0, kas atjaunina datu bāzi līdz v2
  2. tajā paŔā laikā daži pieprasÄ«jumi tika apstrādāti ar versijas instancēm 1.0.0
  3. atjaunināŔana noritēja veiksmÄ«gi, un jums ir vairākas darbÄ«gas versijas lietojumprogrammas instancēs 1.0.0 un pārējās versijas 2.0.0. Visi sazinās ar DB, kas v2
  4. versija 1.0.0 neizmanto datu bāzē kolonnas surname, bet versija 2.0.0 izmanto. Viņi netraucē viens otram, un kļūdām nevajadzētu būt.
  5. versija 2.0.0 saglabā datus gan vecajā, gan jaunajā kolonnā, kas nodroŔina atpakaļsavienojamību.

SvarÄ«gi. Ja jums ir kādi pieprasÄ«jumi, kas skaita elementus, pamatojoties uz vērtÄ«bām no vecās / jaunās kolonnas, jums jāatceras, ka tagad jums ir duplikāti vērtÄ«bās (droÅ”i vien tie joprojām migrē). Piemēram, ja vēlaties saskaitÄ«t lietotājus, kuru uzvārds (kā to sauc) sākas ar burtu A, tad lÄ«dz datu migrācijas pabeigÅ”anai (vecā → jauns kolonna) jums var bÅ«t nekonsistenti dati, ja jÅ«s pieprasāt no jaunās kolonnas.

Lietojumprogrammas atgrieŔana

Tagad mums ir versijas lietojumprogrammas 2.0.0 un datu bāze ir v2.

Soli pa solim:

  1. atgrieziet savu lietojumprogrammu uz versiju 1.0.0.
  2. versija 1.0.0 neizmanto datu bāzē kolonnu uzvārdu, tāpēc atjaunoÅ”anai jābÅ«t veiksmÄ«gai

Izmaiņas DB

DB satur kolonnu ar nosaukumu last_name.

Oriģinālais Flyway skripts:

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

Pievienojuma skripts uzvārdu.

UzmanÄ«bu. Atcerieties, ka NAV JĀPIEVIENO nekādi ierobežojumi NOT NULL pie pievienotās kolonnas. Ja jÅ«s atsaucat JAR, vecā versija nezina par pievienoto kolonnu un automātiski pieŔķirs tam NULL vērtÄ«bu. Ja Ŕāds ierobežojums ir, vecā lietotne vienkārÅ”i sabruks.

-- PIEZĪME: Šai laukam nevar būt NOT NULL ierobežojums, jo, ja jūs atgriezīsities, vecā versija nezina par Ŕo lauku
-- un vienmēr pieŔķirs tam NULL
ALTER TABLE PERSON ADD surname varchar(255);

-- PIEVĒRSÄŖSIMIES, KA TĀ IR ĀTRA MIGRĀCIJA - CITĀGAD MUMS BÅŖTU JĀMIGRĒ PA Å AJĀS DAĻĀS
UPDATE PERSON SET PERSON.surname = PERSON.last_name

Koda izmaiņas

Mēs saglabājam datus kā last_name, tā arÄ« uzvārdu. Tajā paŔā laikā lasam no last_name, jo Ŕī kolonna ir visaktuālāka. IzvietoÅ”anas procesā daži pieprasÄ«jumi varēja tikt apstrādāti ar lietotnes instance, kas vēl nebija atjaunināta.

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

3. solis: no koda noņemt last_name

Lietojumprogrammas versija: 3.0.0

Datu bāzes versija:v3

Komentārs

PiezÄ«me: AcÄ«mredzot oriÄ£inālajā rakstā autors kļūdaini kopējis Ŕī bloka tekstu no 2. soļa. Å ajā solÄ« jāveic izmaiņas lietotnes kodā, kas vērstas uz funkcionalitātes noņemÅ”anu, kas izmanto kolonnu last_name.

Pievienojot jaunu kolonnu un kopējot tās saturu, mēs radÄ«jām uz leju saderÄ«gas izmaiņas DB. Turklāt, ja mēs atsaucam JAR vai mums ir strādājoÅ”s vecais JAR, tas nesabruks izpildes laikā.

Lietojumprogrammas atgrieŔana

Šobrīd mums ir lietotne versijā 3.0.0 un datubāze v3. Versija 3.0.0 neglabā datus last_name. Tas nozīmē, ka uzvārdu satur visjaunāko informāciju.

Soli pa solim:

  1. atgrieziet savu lietojumprogrammu uz versiju 2.0.0.
  2. versija 2.0.0 izmanto un last_name un uzvārdu.
  3. versija 2.0.0 paņems uzvārdu, ja tas nav nulles, citādi —last_name

Datu bāzes izmaiņas

Datubāzē nav struktūras izmaiņu. Tiek izpildīts nākamais skripts, kas veic veco datu galīgo migrāciju:

-- PIEVĒRSÄŖSIMIES, KA TĀ IR ĀTRA MIGRĀCIJA - CITĀGAD MUMS BÅŖTU JĀMIGRĒ PA Å AJĀS DAĻĀS
-- ARÄŖ MĒS NEKONTROLĒJAM, JA MĒS NEPĀRRAUDAMIES VECOS IERAKSTUS. MUMS BÅŖTU JĀSALÄŖDZINA
-- IERAKSTU VERSIJAS, LAI PĀRLIECINĀTU, KA, JA JAU IR IERAKSTS AR AUGSTĀKU VERSIJAS NUMURU
-- MĒS NEPĀRRAUDAMIES TO.
UPDATE PERSON SET PERSON.surname = PERSON.last_name;

-- NOŅEMOT NOT NULL IEROBEŽOJUMU; CITĀGAD TIEM MĒS MĒĶAM IEVADĀT NULL VĒRTÄŖBU LAST_NAME
-- AR NOT NULL IEROBEŽOJUMU.
ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;

Koda izmaiņas

PiezÄ«me: Å Ä« bloka apraksts arÄ« tika kļūdaini kopēts autoram no 2. soļa. Saskaņā ar raksta loÄ£iku, izmaiņas kodā Å”ajā solÄ« jābÅ«t vērstām uz elementu noņemÅ”anu, kas strādā ar kolonnu last_name.

Mēs glabājam datus kā last_name, tā arÄ« surname. Turklāt mēs lasām no kolonnas last_name, jo tā ir visatbilstoŔākā. IzvietoÅ”anas procesā daži pieprasÄ«jumi var tikt apstrādāti ar instanci, kas vēl nav atjaunināta.

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

4. solis: Dzēst last_name no DB

Lietojumprogrammas versija: 4.0.0

Datu bāzes versija: v4

Komentārs

Tā kā versijas kods 3.0.0 neizmantot kolonu last_name, izpildes laikā nekas slikts nenotiks, ja mēs atkāpsimies lÄ«dz 3.0.0 pēc kolonnas dzēŔanas no datubāzes.

Izpildes skriptu žurnāli

Mēs to darÄ«sim Ŕādā veidā:

01) Palaižam 1.0.0
02) Gaidām, kad lietotne (1.0.0) ieslēgsies
03) Ģenerējam personu, izsaucot POST localhost:9991/person uz versiju 1.0.0
04) Palaižam 2.0.0
05) Gaidām, kad lietotne (2.0.0) ieslēgsies
06) Ģenerējam personu, izsaucot POST localhost:9991/person uz versiju 1.0.0
07) Ģenerējam personu, izsaucot POST localhost:9992/person uz versiju 2.0.0
08) Nogalinām lietotni (1.0.0)
09) Palaižam 3.0.0
10) Gaidām, kad lietotne (3.0.0) ieslēgsies
11) Ģenerējam personu, izsaucot POST localhost:9992/person uz versiju 2.0.0
12) Ģenerējam personu, izsaucot POST localhost:9993/person uz versiju 3.0.0
13) Nogalinām lietotni (3.0.0)
14) Palaižam 4.0.0
15) Gaidām, kad lietotne (4.0.0) ieslēgsies
16) Ģenerējam personu, izsaucot POST localhost:9993/person uz versiju 3.0.0
17) Ģenerējam personu, izsaucot POST localhost:9994/person uz versiju 4.0.0

Sākam lietotni versijā 1.0.0
Ģenerējam personu versijā 1.0.0
Sūtot post uz 127.0.0.1:9991/person. Šī ir atbilde:

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

Sākam lietotni versijā 2.0.0

Ģenerējam personu versijā 1.0.0
Sūtot post uz 127.0.0.1:9991/person. Šī ir atbilde:

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

Ģenerējam personu versijā 2.0.0
Sūtot post uz 127.0.0.1:9992/person. Šī ir atbilde:

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

Nogalinām lietotni 1.0.0

Sākam lietotni versijā 3.0.0

Ģenerējam personu versijā 2.0.0
Sūtot post uz 127.0.0.1:9992/person. Šī ir atbilde:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}

Ģenerējam personu versijā 3.0.0
Sūtot post uz 127.0.0.1:9993/person. Šī ir atbilde:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}

Nogalinām lietotni 2.0.0

Sākam lietotni versijā 4.0.0

Ģenerējam personu versijā 3.0.0
Sūtot post uz 127.0.0.1:9993/person. Šī ir atbilde:

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

Ģenerējam personu versijā 4.0.0
Sūtot post uz 127.0.0.1:9994/person. Šī ir atbilde:

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

Izmaiņas DB

AttiecÄ«bā uz v3 mēs vienkārÅ”i dzēŔam kolonnu last_name un pievienojam trÅ«kstoÅ”os ierobežojumus.

-- DZEST KOLONU
ALTER TABLE PERSON DROP last_name;

-- PIEVIENOT IEROBEŽOJUMUS
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;

Koda izmaiņas

Izmaiņas kodā nav.

Izvade

Mēs veiksmīgi pielāgojām atpakaļ nesaderīgu kolonnas nosaukuma izmaiņu, veicot vairākus atpakaļ saderīgus izvietojumus. Zemāk ir veikto darbību kopsavilkums:

  1. izvietojums lietotnes versija 1.0.0 ar v1 DB shēma (kolonnas nosaukums = last_name)
  2. izvietojums lietotnes versija 2.0.0, kas saglabā datus last_name un uzvārdu. Lietotne lasa no last_name. DB atrodas versijā v2, kas satur kolonnas kā last_name, gan uzvārds. uzvārds ir kopija last_name. (Piezīme: Ŕim laukam nedrīkst būt ierobežojums not null)
  3. izvietojums lietotnes versija 3.0.0, kas saglabā datus tikai uzvārdu un lasa no uzvārda. AttiecÄ«bā uz DB notiek pēdējā migrācija last_name par uzvārdu. ArÄ« ierobežojums NOT NULL tiek noņemts no last_name. DB paÅ”laik ir versijā v3
  4. izvietojums lietotnes versija 4.0.0 — kodā netiek izdarÄ«tas nekādas izmaiņas. Datubāzes izvietoÅ”ana v4, kas dzēŔ last_name. Å eit jÅ«s varat pievienot jebkādus trÅ«kstoÅ”os ierobežojumus DB.

Izmantojot Å”o pieeju, jÅ«s vienmēr varat atgriezties uz vienu versiju atpakaļ, nesabojājot datu bāzes / lietotnes saderÄ«bu.

Kods

Visi kods, kas izmantots Ŕajā rakstā, ir pieejams Github. Zemāk ir papildu apraksts.

Projektu

Pēc repozitorija klonēŔanas jÅ«s redzēsiet sekojoÅ”o mapju struktÅ«ru.

ā”œā”€ā”€ boot-flyway-v1              - 1.0.0 versija lietotnei ar v1 shēmu
ā”œā”€ā”€ boot-flyway-v2              - 2.0.0 versija lietotnei ar v2 shēmu (atpakaļsaderÄ«ga - lietotni var atgriezt)
ā”œā”€ā”€ boot-flyway-v2-bad          - 2.0.0.BAD versija lietotnei ar v2bad shēmu (neatpakaļsaderÄ«ga - lietotni nevar atgriezt)
ā”œā”€ā”€ boot-flyway-v3              - 3.0.0 versija lietotnei ar v3 shēmu (lietotni var atgriezt)
└── boot-flyway-v4              - 4.0.0 versija lietotnei ar v4 shēmu (lietotni var atgriezt)

Skripti

JÅ«s varat palaist scenārijus, kas aprakstÄ«ti zemāk esoÅ”ajos skriptos, kas demonstrējot atpakaļ saderÄ«gas un nesaderÄ«gas izmaiņas DB.

Lai redzētu gadījumu ar atpakaļ saderīgām izmaiņām, palaižiet:

.\/scripts\/scenario_backward_compatible.sh

Un lai redzētu gadījumu ar atpakaļ nesaderīgām izmaiņām, palaižiet:

.\/scripts\/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

Visi piemēri ņemti no Spring Boot Sample Flyway.

Jūs varat aplūkot http://localhost:8080/flyway, tur ir skriptu saraksts.

Å is piemērs arÄ« iekļauj H2 konsoli (adresē http://localhost:8080/h2-console), lai jÅ«s varētu apskatÄ«t datu bāzes stāvokli (noklusējuma jdbc URL — jdbc:h2:mem:testdb).

Papildus

Izlasiet arī citus rakstus mūsu emuārā:

Avots: habr.com

Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster