Zbatimi dhe bazat e të dhënave zero për kohën e ndërprerjes

Zbatimi dhe bazat e të dhënave zero për kohën e ndërprerjes

Ky artikull shpjegon në detaje se si të zgjidhen çështjet e përputhshmërisë së bazës së të dhënave në vendosje. Ne do t'ju tregojmë se çfarë mund të ndodhë me aplikacionet tuaja të prodhimit nëse përpiqeni të vendosni pa përgatitje paraprake. Më pas do të kalojmë nëpër fazat e ciklit jetësor të aplikacionit që kërkohet të ketë zero kohë joproduktive (përafërsisht. korsi: më tej - zero joproduktive). Rezultati i operacioneve tona do të jetë aplikimi i ndryshimit të bazës së të dhënave të papajtueshme me prapavijën në një mënyrë të përputhshme me prapavijën.

Nëse dëshironi të kuptoni shembujt e kodit nga artikulli, mund t'i gjeni në GitHub.

Paraqitje

Zbarkim zero joproduktive

Çfarë mistike vendosje zero joproduktive? Ju mund të thoni se kjo është kur aplikacioni juaj vendoset në atë mënyrë që ju mund të prezantoni me sukses një version të ri të aplikacionit në prodhim, ndërsa përdoruesi nuk e vëren mosdisponueshmërinë e tij. Nga perspektiva e përdoruesit dhe e kompanisë, ky është skenari më i mirë i mundshëm i vendosjes sepse lejon futjen e veçorive të reja dhe rregullimin e gabimeve pa ndërprerje.

Si të arrihet kjo? Ka disa mënyra, këtu është një prej tyre:

  • vendos versionin nr. 1 të shërbimit tuaj
  • kryeni një migrim të bazës së të dhënave
  • Vendosni versionin #2 të shërbimit tuaj paralelisht me versionin #1
  • Sapo të shihni se versioni nr. 2 funksionon siç duhet, hiqni versionin nr. 1
  • gati!

Lehtë, apo jo? Fatkeqësisht, nuk është aq e thjeshtë dhe do ta shikojmë në detaje më vonë. Tani le të kontrollojmë një proces tjetër mjaft të zakonshëm të vendosjes - vendosjen e gjelbër blu.

A keni dëgjuar ndonjëherë për vendosja e gjelbër blu? Cloud Foundry e bën këtë jashtëzakonisht të lehtë. Vetëm shikoni Ky artikull, ku e përshkruajmë këtë në mënyrë më të detajuar. Për ta përmbledhur shkurtimisht, le t'ju kujtojmë se si të bëni vendosjen e gjelbër blu:

  • sigurohuni që të funksionojnë dy kopje të kodit tuaj të prodhimit (“blu” dhe “jeshile”);
  • drejtoje të gjithë trafikun në mjedisin blu, d.m.th. në mënyrë që URL-të e prodhimit të tregojnë atje;
  • vendos dhe teston të gjitha ndryshimet e aplikacionit në një mjedis të gjelbër;
  • ndërroni url-të nga mjedisi blu në jeshil

Vendosja e gjelbër blu është një qasje që ju lejon të prezantoni lehtësisht veçori të reja pa u shqetësuar për prishjen e prodhimit. Kjo për faktin se edhe nëse ndodh diçka, ju mund të ktheheni lehtësisht në mjedisin e mëparshëm thjesht "duke goditur një çelës".

Pasi të keni lexuar të gjitha sa më sipër, mund të bëni pyetjen: Çfarë lidhje ka zero joproduktive me vendosjen e gjelbër blu?

Epo, ato kanë mjaft të përbashkëta, pasi mbajtja e dy kopjeve të të njëjtit mjedis kërkon përpjekje të dyfishtë për t'i ruajtur ato. Kjo është arsyeja pse disa ekipe pretendojnë Martin Fowler, ndiqni një variacion të kësaj qasjeje:

Një tjetër mundësi është të përdorni të njëjtën bazë të dhënash, duke krijuar çelësa blu-jeshile për shtresat e ueb-it dhe të domenit. Në këtë qasje, baza e të dhënave shpesh mund të jetë problem, veçanërisht kur duhet të ndryshoni skemën e saj për të mbështetur një version të ri të softuerit.

Dhe këtu kemi ardhur te problemi kryesor në këtë artikull. bazës së të dhënave. Le t'i hedhim një vështrim tjetër kësaj fraze.

kryeni një migrim të bazës së të dhënave.

Tani duhet t'i bëni vetes pyetjen - çka nëse ndryshimi i bazës së të dhënave nuk është i përputhshëm me të prapme? A nuk do të prishet versioni im i parë i aplikacionit? Në fakt, kjo është pikërisht ajo që do të ndodhë...

Pra, edhe përkundër përfitimeve të mëdha të kohës së ndërprerjes zero / vendosjes së gjelbër blu, kompanitë priren të ndjekin procesin e mëposhtëm më të sigurt për vendosjen e aplikacioneve të tyre:

  • përgatitni një paketë me një version të ri të aplikacionit
  • mbyllni një aplikacion që funksionon
  • ekzekutoni skriptet për të migruar bazën e të dhënave
  • vendos dhe lëshon një version të ri të aplikacionit

Në këtë artikull, ne do të detajojmë se si mund të punoni me bazën e të dhënave dhe kodin tuaj për të përfituar nga vendosja zero joproduktive.

Problemet e bazës së të dhënave

Nëse keni një aplikacion pa shtetësi që nuk ruan asnjë të dhënë në bazën e të dhënave, mund të merrni zero vendosje joproduktive menjëherë. Fatkeqësisht, shumica e programeve kompjuterike duhet të ruajnë të dhënat diku. Kjo është arsyeja pse duhet të mendoni dy herë përpara se të bëni ndonjë ndryshim në qark. Përpara se të futemi në detajet se si të ndryshojmë skemën në mënyrë që vendosja pa ndërprerje të jetë e mundur, le të përqendrohemi së pari në skemën e versionimit.

Skema e versionimit

Në këtë artikull do të përdorim Rrugë fluturimi si një mjet kontrolli versioni (përafërsisht. Përkthimi: ne po flasim për migrimet e bazës së të dhënave). Natyrisht, ne do të shkruajmë gjithashtu një aplikacion Spring Boot që ka mbështetje të integruar Flyway dhe do të kryejë migrimin e skemës gjatë konfigurimit të kontekstit të aplikacionit. Kur përdorni Flyway, mund të ruani skriptet e migrimit në dosjen e projekteve tuaja (si parazgjedhje në classpath:db/migration). Këtu mund të shihni një shembull të skedarëve të tillë migrimi

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

Në këtë shembull shohim 4 skripta migrimi që, nëse nuk ekzekutohen më parë, do të ekzekutohen njëri pas tjetrit kur të fillojë aplikacioni. Le të shohim një nga skedarët (V1__init.sql) si nje shembull.

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

Gjithçka është krejtësisht e vetëshpjegueshme: ju mund të përdorni SQL për të përcaktuar se si duhet modifikuar databaza juaj. Për më shumë informacion rreth Spring Boot dhe Flyway, shikoni Dokumentet e nisjes së pranverës.

Duke përdorur një mjet kontrolli burimi me Spring Boot, ju merrni 2 përfitime të mëdha:

  • ju ndani ndryshimet e bazës së të dhënave nga ndryshimet e kodit
  • Migrimi i bazës së të dhënave ndodh së bashku me paraqitjen e aplikacionit tuaj, d.m.th. procesi juaj i vendosjes është thjeshtuar

Zgjidhja e problemeve të bazës së të dhënave

Në pjesën tjetër të artikullit, ne do të përqendrohemi në shikimin e dy qasjeve për ndryshimet e bazës së të dhënave.

  • papajtueshmëria e prapambetur
  • përputhshmëria e prapambetur

E para do të konsiderohet si një paralajmërim se nuk duhet të kryeni vendosjen e kohës së ndërprerjes zero pa përgatitje paraprake... E dyta ofron një zgjidhje se si mund të kryeni një vendosje pa ndërprerje dhe në të njëjtën kohë të ruani përputhshmërinë e prapambetur.

Projekti ynë për të cilin do të punojmë do të jetë një aplikacion i thjeshtë Spring Boot Flyway që ka Person с first_name и last_name në bazën e të dhënave (përafërsisht. përkthimi: Person është një tabelë dhe first_name и last_name - këto janë fushat në të). Ne duam të riemërtojmë last_name в surname.

Supozimet

Përpara se të futemi në detaje, ka disa supozime që duhet të bëjmë në lidhje me aplikacionet tona. Rezultati kryesor që duam të arrijmë do të jetë një proces mjaft i thjeshtë.

Shënimi. Biznes PRO-KËSHILLA. Thjeshtimi i proceseve mund t'ju kursejë shumë para në mbështetje (sa më shumë njerëz të keni që punojnë për kompaninë tuaj, aq më shumë para mund të kurseni)!

Nuk ka nevojë për të rikthyer bazën e të dhënave

Kjo thjeshton procesin e vendosjes (disa rikthime të bazës së të dhënave janë pothuajse të pamundura, siç është rikthimi i fshirjes). Ne preferojmë të rikthejmë vetëm aplikacionet. Në këtë mënyrë, edhe nëse keni baza të të dhënave të ndryshme (për shembull, SQL dhe NoSQL), tubacioni juaj i vendosjes do të duket i njëjtë.

Duhet të jetë GJITHMONË e mundur që aplikacioni të kthehet një version prapa (jo më shumë)

Rikthimi duhet të bëhet vetëm kur është e nevojshme. Nëse ka një gabim në versionin aktual që nuk rregullohet lehtë, duhet të jemi në gjendje të kthehemi në versionin më të fundit të punës. Supozojmë se ky version i fundit i punës është ai i mëparshmi. Ruajtja e përputhshmërisë së kodit dhe bazës së të dhënave për më shumë se një paraqitje do të ishte jashtëzakonisht e vështirë dhe e shtrenjtë.

Shënimi. Për lexueshmëri më të madhe, në këtë artikull do të ndryshojmë versionin kryesor të aplikacionit.

Hapi 1: Gjendja fillestare

Versioni i aplikacionit: 1.0.0
Versioni i DB: v1

Koment

Kjo do të jetë gjendja fillestare e aplikacionit.

Ndryshimet e bazës së të dhënave

DB përmban 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');

Ndryshimet e kodit

Aplikacioni ruan të dhënat e personit në 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
                + "]";
    }
}

Riemërtimi i kolonës i papajtueshëm prapa

Le të shohim një shembull se si të ndryshojmë emrin e një kolone:

Vëmendje. Shembulli i mëposhtëm do t'i prishë gjërat qëllimisht. Ne e tregojmë këtë për të demonstruar problemin e përputhshmërisë së bazës së të dhënave.

Versioni i aplikacionit: 2.0.0.BAD

Versioni i DB: v2bad

Koment

Ndryshimet aktuale NUK na lejojnë të ekzekutojmë dy instanca (të vjetra dhe të reja) në të njëjtën kohë. Kështu, vendosja e kohës së ndërprerjes zero do të jetë e vështirë të arrihet (nëse merren parasysh supozimet, në fakt është e pamundur).

Testimi A/B

Situata aktuale është se ne kemi një version aplikacioni 1.0.0, të vendosura në prodhim dhe në bazën e të dhënave v1. Duhet të vendosim një shembull të dytë të aplikacionit, version 2.0.0.BAD, dhe përditësoni bazën e të dhënave në v2bad.

hapa:

  1. vendoset një shembull i ri i aplikacionit të versionit 2.0.0.BADi cili përditëson bazën e të dhënave në v2bad
  2. në bazën e të dhënave v2bad kolonë last_name nuk ekziston më - u ndryshua në surname
  3. Përditësimi i bazës së të dhënave dhe aplikacionit ishte i suksesshëm dhe disa raste janë duke u ekzekutuar 1.0.0, të tjerët - në 2.0.0.BAD. Gjithçka është e lidhur me bazën e të dhënave v2bad
  4. të gjitha rastet e versionit 1.0.0 do të fillojnë të hedhin gabime sepse do të përpiqen të fusin të dhëna në kolonë last_nameqë nuk ekziston më
  5. të gjitha rastet e versionit 2.0.0.BAD do të funksionojë pa probleme

Siç mund ta shihni, nëse bëjmë ndryshime të papajtueshme prapa në bazën e të dhënave dhe aplikacionit, testimi A/B është i pamundur.

Rikthimi i aplikacionit

Le të supozojmë se pasi të përpiqemi të bëjmë vendosjen e A/B (përafërsisht. per.: autori ndoshta ka menduar këtu testimin A/B) vendosëm që duhet ta kthejmë aplikacionin në version 1.0.0. Le të themi se nuk duam të rikthejmë bazën e të dhënave.

hapa:

  1. ne ndalojmë shembullin e aplikacionit të versionit 2.0.0.BAD
  2. baza e të dhënave është ende v2bad
  3. që nga versioni 1.0.0 nuk e kupton se çfarë është surname, do të shohim gabime
  4. ferri ka dalë, nuk mund të kthehemi më

Siç mund ta shihni, nëse bëjmë ndryshime të papajtueshme në bazën e të dhënave dhe aplikacionit, nuk mund të kthehemi në versionin e mëparshëm.

Regjistrat e ekzekutimit të skriptit

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

Ndryshimet e bazës së të dhënave

Skripti i migrimit që riemëron last_name в surname

Burimi i skenarit 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 që riemëron last_name.

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

Ndryshimet e kodit

Ne kemi ndryshuar emrin e fushës lastName mbi surname.

Riemërtimi i një kolone në një mënyrë të përputhshme me prapavijën

Kjo është situata më e zakonshme që mund të hasim. Ne duhet të bëjmë ndryshime të papajtueshme prapa. Ne kemi vërtetuar tashmë se për vendosjen me kohë jo të ndërprerjes, nuk duhet të aplikojmë thjesht migrimin e bazës së të dhënave pa hapa shtesë. Në këtë pjesë të artikullit, ne do të kryejmë 3 vendosje të aplikacionit së bashku me migrimet e bazës së të dhënave për të arritur rezultatin e dëshiruar duke ruajtur përputhshmërinë e prapambetur.

Shënimi. Kujtojmë që ne kemi një bazë të dhënash versionesh v1. Ai përmban kolona first_name и last_name. Ne duhet të ndryshojmë last_name mbi surname. Kemi edhe versionin e aplikacionit 1.0.0, e cila ende nuk është përdorur surname.

Hapi 2: Shtoni mbiemrin

Versioni i aplikacionit: 2.0.0
Versioni i DB: v2

Koment

Duke shtuar një kolonë të re dhe duke kopjuar përmbajtjen e saj, ne krijojmë ndryshime të pajtueshme në bazën e të dhënave. Në të njëjtën kohë, nëse e rikthejmë JAR-in ose kemi një JAR të vjetër që funksionon, ai nuk do të prishet gjatë ekzekutimit.

Ne po nxjerrim një version të ri

hapa:

  1. kryeni një migrim të bazës së të dhënave për të krijuar një kolonë të re surname. Tani versioni juaj DB v2
  2. kopjoni të dhënat nga last_name в surname. Обратите вниманиеqë nëse keni shumë nga këto të dhëna, duhet të merrni parasysh migrimin e grupeve!
  3. shkruani kodin ku janë përdorur TË DYJA и i riDhe старый kolonë. Tani versioni i aplikacionit tuaj 2.0.0
  4. lexoni vlerën nga kolona surname, nëse nuk është null, ose nga last_name, nëse surname e paspecifikuar. Ju mund të fshini getLastName() nga kodi, pasi ai do të dalë null kur ktheni aplikacionin tuaj nga 3.0.0 tek 2.0.0.

Nëse jeni duke përdorur Spring Boot Flyway, këto dy hapa do të kryhen gjatë fillimit të versionit 2.0.0 aplikacionet. Nëse e përdorni manualisht mjetin e versionimit të bazës së të dhënave, do t'ju duhet të bëni dy gjëra të ndryshme për ta bërë këtë (së pari përditësoni versionin db manualisht dhe më pas vendosni aplikacionin e ri).

E rëndësishme Mos harroni se kolona e krijuar rishtazi NUK DUHET të jetë JO NULL. Nëse bëni një rikthim, aplikacioni i vjetër nuk di për kolonën e re dhe nuk do ta instalojë atë gjatë Insert. Por nëse shtoni këtë kufizim dhe db juaj do të jetë v2, kjo do të kërkojë vendosjen e vlerës së kolonës së re. Që do të çojë në shkelje të kufizimeve.

E rëndësishme Ju duhet të hiqni metodën getLastName(), sepse në version 3.0.0 Nuk ka asnjë koncept të një kolone në kod last_name. Kjo do të thotë se null do të vendoset atje. Mund të lini metodën dhe të shtoni kontrolle për null, por një zgjidhje shumë më e mirë do të ishte të sigurohesh që në logjikë getSurname() keni zgjedhur vlerën e saktë jo zero.

Testimi A/B

Situata aktuale është se ne kemi një version aplikacioni 1.0.0, i vendosur në prodhim dhe baza e të dhënave në v1. Duhet të vendosim një shembull të dytë të aplikacionit të versionit 2.0.0e cila do të përditësojë bazën e të dhënave në v2.

hapa:

  1. vendoset një shembull i ri i aplikacionit të versionit 2.0.0i cili përditëson bazën e të dhënave në v2
  2. ndërkohë disa kërkesa u përpunuan nga instancat e versionit 1.0.0
  3. përditësimi ishte i suksesshëm dhe ju keni disa raste të ekzekutimit të aplikacionit të versionit 1.0.0 dhe versione të tjera 2.0.0. Të gjithë komunikojnë me bazën e të dhënave në v2
  4. version 1.0.0 nuk përdor kolonën e mbiemrit në bazën e të dhënave, por versionin 2.0.0 përdor. Ata nuk ndërhyjnë me njëri-tjetrin dhe nuk duhet të ketë gabime.
  5. version 2.0.0 ruan të dhënat si në kolonën e vjetër ashtu edhe në atë të re, duke siguruar përputhshmëri të prapambetur

E rëndësishme Nëse keni ndonjë pyetje që numëron artikujt bazuar në vlerat nga kolona e vjetër/e re, duhet të mbani mend se tani keni vlera të kopjuara (ka shumë të ngjarë që ato janë ende duke migruar). Për shembull, nëse dëshironi të numëroni numrin e përdoruesve, mbiemri i të cilëve (sido që të quhet kolona) fillon me shkronjën A, pastaj derisa të përfundojë migrimi i të dhënave (oldnew kolonë) mund të keni të dhëna jokonsistente nëse kërkoni një kolonë të re.

Rikthimi i aplikacionit

Tani kemi versionin e aplikacionit 2.0.0 dhe baza e të dhënave në v2.

hapa:

  1. kthejeni aplikacionin tuaj në version 1.0.0.
  2. version 1.0.0 nuk përdor një kolonë në bazën e të dhënave surname, kështu që rikthimi duhet të jetë i suksesshëm

DB ndryshon

Baza e të dhënave përmban një kolonë me emrin last_name.

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

Shto skript surname.

Vëmendje. Mos harroni se ju NUK MUND TË SHTONI asnjë kufizim NOT NULL në kolonën që po shtoni. Nëse e ktheni prapa JAR-in, versioni i vjetër nuk do të ketë asnjë ide për kolonën e shtuar dhe do ta vendosë automatikisht në NULL. Nëse ekziston një kufizim i tillë, aplikacioni i vjetër thjesht do të prishet.

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

Ndryshimet e kodit

Ne ruajmë të dhënat si last_name, dhe në surname. Në të njëjtën kohë lexojmë nga last_name, pasi kjo kolonë është më e rëndësishme. Gjatë procesit të vendosjes, disa kërkesa mund të jenë përpunuar nga një shembull aplikacioni që nuk është përditësuar ende.

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

Hapi 3: Heqja e mbiemrit nga kodi

Versioni i aplikacionit: 3.0.0

Versioni i DB:v3

Koment

shënim per.: Me sa duket, në artikullin origjinal autori gabimisht kopjoi tekstin e këtij blloku nga hapi 2. Në këtë hap, duhet të bëhen ndryshime në kodin e aplikacionit që synojnë heqjen e funksionalitetit që përdor kolonën last_name.

Duke shtuar një kolonë të re dhe duke kopjuar përmbajtjen e saj, ne krijuam ndryshime të pajtueshme në bazën e të dhënave. Gjithashtu, nëse e kthejmë JAR-in ose kemi një JAR të vjetër që funksionon, ai nuk do të prishet gjatë ekzekutimit.

Rikthimi i aplikacionit

Aktualisht kemi versionin e aplikacionit 3.0.0 dhe bazës së të dhënave v3. Version 3.0.0 nuk ruan të dhënat në last_name. Kjo do të thotë se në surname ruhet informacioni më i përditësuar.

hapa:

  1. kthejeni aplikacionin tuaj në version 2.0.0.
  2. version 2.0.0 përdor dhe last_name и surname.
  3. version 2.0.0 do të marrë surname, nëse nuk është zero, përndryshe -last_name

Ndryshimet e bazës së të dhënave

Nuk ka ndryshime strukturore në bazën e të dhënave. Skripti i mëposhtëm ekzekutohet për të kryer migrimin përfundimtar të të dhënave të vjetra:

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

Ndryshimet e kodit

shënim per.: Përshkrimi i këtij blloku gjithashtu u kopjua gabimisht nga autori nga hapi 2. Në përputhje me logjikën e artikullit, ndryshimet në kod në këtë hap duhet të synojnë heqjen prej tij të elementeve që punojnë me kolonën last_name.

Ne ruajmë të dhënat si last_name, dhe në surname. Për më tepër, lexojmë nga kolona last_name, pasi është më e rëndësishmja. Gjatë procesit të vendosjes, disa kërkesa mund të përpunohen nga një shembull që nuk është përmirësuar ende.

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

Hapi 4: Heqja e mbiemrit nga baza e të dhënave

Versioni i aplikacionit: 4.0.0

Versioni i DB: v4

Koment

Për faktin se kodi i versionit 3.0.0 nuk e përdori kolonën last_name, asgjë e keqe nuk do të ndodhë gjatë ekzekutimit nëse kthehemi përsëri në 3.0.0 pas heqjes së një kolone nga baza e të dhënave.

Regjistrat e ekzekutimit të skriptit

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 ndryshon

për v3 thjesht heqim kolonën last_name dhe shtoni kufizime që mungojnë.

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

Ndryshimet e kodit

Nuk ka ndryshime në kod.

Prodhim

Zbatuam me sukses një ndryshim të emrit të kolonës të papajtueshëm me prapavijë duke kryer disa vendosje të pajtueshme me prapavijën. Më poshtë është një përmbledhje e veprimeve të kryera:

  1. vendosja e versionit të aplikacionit 1.0.0 с v1 skema e bazës së të dhënave (emri i kolonës = last_name)
  2. vendosja e versionit të aplikacionit 2.0.0, i cili ruan të dhënat në last_name и surname. Aplikacioni lexon nga last_name. Baza e të dhënave është në version v2që përmban kolona si last_nameDhe surname. surname është një kopje e last_name. (SHËNIM: Kjo kolonë nuk duhet të ketë një kufizim jo null)
  3. vendosja e versionit të aplikacionit 3.0.0, i cili ruan vetëm të dhënat në surname dhe lexon nga mbiemri. Sa i përket bazës së të dhënave, migrimi i fundit po ndodh last_name в surname. Gjithashtu një kufizim JO NULL hequr nga last_name. Baza e të dhënave është tani në version v3
  4. vendosja e versionit të aplikacionit 4.0.0 - nuk janë bërë ndryshime në kod. Vendosja e bazës së të dhënave v4, e cila heq last_name. Këtu mund të shtoni çdo kufizim që mungon në bazën e të dhënave.

Duke ndjekur këtë qasje, gjithmonë mund të rikthehet një version pa prishur pajtueshmërinë e bazës së të dhënave/aplikacionit.

Kod

I gjithë kodi i përdorur në këtë artikull është i disponueshëm në Github. Më poshtë është një përshkrim shtesë.

projektet

Pas klonimit të depove, do të shihni strukturën e mëposhtme të dosjeve.

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

Skenare

Ju mund të ekzekutoni skriptet e përshkruara në skriptet më poshtë, të cilat do të demonstrojnë ndryshime të përputhshme dhe të papajtueshme në bazën e të dhënave.

Të shikosh rasti me ndryshime të përputhshme prapa, vraponi:

./scripts/scenario_backward_compatible.sh

Dhe për të parë rast me ndryshime të papajtueshme prapa, vraponi:

./scripts/scenario_backward_incompatible.sh

Shembull i çizmeve pranverore Flyway

Të gjithë shembujt janë marrë nga Spring Boot Sample Flyway.

Ju mund t'i hidhni një sy http://localhost:8080/flyway, ekziston një listë skriptesh.

Ky shembull përfshin gjithashtu konsolën H2 (në http://localhost:8080/h2-console) kështu që ju mund të shikoni statusin e bazës së të dhënave (URL e parazgjedhur jdbc është jdbc:h2:mem:testdb).

Përveç kësaj

Lexoni gjithashtu artikuj të tjerë në blogun tonë:

Burimi: www.habr.com

Shto një koment