Zero Downtime Deployment at Mga Database

Zero Downtime Deployment at Mga Database

Ipinapaliwanag ng artikulong ito nang detalyado kung paano lutasin ang mga isyu sa compatibility ng database sa pag-deploy. Sasabihin namin sa iyo kung ano ang maaaring mangyari sa iyong mga application sa produksyon kung susubukan mong mag-deploy nang walang paunang paghahanda. Pagkatapos ay dadaan tayo sa mga yugto ng lifecycle ng application na kinakailangang magkaroon ng zero downtime (tinatayang lane: karagdagang - zero downtime). Ang resulta ng aming mga operasyon ay ilapat ang pabalik-hindi tugmang pagbabago sa database sa isang pabalik-katugmang paraan.

Kung gusto mong maunawaan ang mga halimbawa ng code mula sa artikulo, mahahanap mo ang mga ito sa GitHub.

Pagpapakilala

Zero downtime deployment

Anong mystical zero downtime deployment? Maaari mong sabihin na ito ay kapag ang iyong application ay na-deploy sa paraang matagumpay mong maipakilala ang isang bagong bersyon ng application sa produksyon, habang ang user ay hindi napapansin ang kawalan nito. Mula sa pananaw ng user at kumpanya, ito ang pinakamahusay na posibleng senaryo sa pag-deploy dahil pinapayagan nitong maipakilala ang mga bagong feature at maayos ang mga bug nang walang pagkaantala.

Paano ito makakamit? Mayroong ilang mga paraan, narito ang isa sa mga ito:

  • i-deploy ang bersyon No. 1 ng iyong serbisyo
  • magsagawa ng paglilipat ng database
  • I-deploy ang bersyon #2 ng iyong serbisyo kasabay ng bersyon #1
  • sa sandaling makita mong gumagana ang bersyon No. 2 ayon sa nararapat, alisin ang bersyon No. 1
  • handa na!

Madali lang, di ba? Sa kasamaang palad, hindi ito ganoon kasimple, at titingnan natin iyon nang detalyado sa ibang pagkakataon. Ngayon tingnan natin ang isa pang medyo karaniwang proseso ng deployment - blue green deployment.

Narinig mo na ba tungkol sa asul na berdeng deployment? Pinapadali ito ng Cloud Foundry. Tingnan mo lang artikulong ito, kung saan inilalarawan namin ito nang mas detalyado. Upang maikling buod, hayaan mong ipaalala namin sa iyo kung paano gawin ang asul na berdeng deployment:

  • tiyaking gumagana ang dalawang kopya ng iyong production code (“asul” at “berde”);
  • idirekta ang lahat ng trapiko sa asul na kapaligiran, i.e. upang ang mga URL ng produksyon ay tumuturo doon;
  • i-deploy at subukan ang lahat ng mga pagbabago sa application sa isang berdeng kapaligiran;
  • lumipat ng mga url mula sa asul patungo sa berdeng kapaligiran

Ang pag-deploy ng asul na berde ay isang diskarte na nagbibigay-daan sa iyong madaling magpakilala ng mga bagong feature nang hindi nababahala tungkol sa pagkasira ng produksyon. Ito ay dahil sa katotohanan na kahit na may mangyari, madali kang makakabalik sa dating kapaligiran sa pamamagitan lamang ng "pag-flick ng switch."

Pagkatapos basahin ang lahat ng nasa itaas, maaari mong itanong ang tanong: Ano ang kinalaman ng zero downtime sa Blue green deployment?

Well, marami silang pagkakatulad, dahil ang pagpapanatili ng dalawang kopya ng parehong kapaligiran ay nangangailangan ng dobleng pagsisikap upang mapanatili ang mga ito. Ito ang dahilan kung bakit inaangkin ng ilang mga koponan Martin Fowler, sundin ang isang pagkakaiba-iba ng diskarteng ito:

Ang isa pang pagpipilian ay ang paggamit ng parehong database, na lumilikha ng mga asul-berdeng switch para sa web at mga layer ng domain. Sa ganitong paraan, ang database ay kadalasang maaaring maging problema, lalo na kapag kailangan mong baguhin ang schema nito upang suportahan ang isang bagong bersyon ng software.

At narito tayo sa pangunahing problema sa artikulong ito. Database. Tingnan natin muli ang pariralang ito.

magsagawa ng paglilipat ng database.

Ngayon kailangan mong tanungin ang iyong sarili ng tanong - paano kung ang pagbabago ng database ay hindi tugma sa likuran? Hindi ba masisira ang aking unang bersyon ng app? Sa katunayan, ito mismo ang mangyayari...

Kaya, kahit na sa kabila ng malaking benepisyo ng zero downtime / blue green deployment, ang mga kumpanya ay may posibilidad na sundin ang sumusunod na mas ligtas na proseso para sa pag-deploy ng kanilang mga application:

  • maghanda ng package na may bagong bersyon ng application
  • isara ang isang tumatakbong application
  • magpatakbo ng mga script para i-migrate ang database
  • mag-deploy at maglunsad ng bagong bersyon ng application

Sa artikulong ito, idedetalye namin kung paano ka makakagamit ng iyong database at code para samantalahin ang zero downtime deployment.

Mga problema sa database

Kung mayroon kang stateless na application na hindi nag-iimbak ng anumang data sa database, maaari kang makakuha ng zero downtime deployment kaagad. Sa kasamaang palad, ang karamihan sa software ay kailangang mag-imbak ng data sa isang lugar. Ito ang dahilan kung bakit dapat kang mag-isip nang dalawang beses bago gumawa ng anumang mga pagbabago sa circuit. Bago natin talakayin ang mga detalye kung paano baguhin ang schema nang sa gayon ay posible ang pag-deploy ng walang downtime, tumuon muna tayo sa schema ng bersyon.

scheme ng bersyon

Sa artikulong ito ay gagamitin natin Flyway bilang tool sa pagkontrol ng bersyon (tinatayang Pagsasalin: pinag-uusapan natin ang tungkol sa paglilipat ng database). Naturally, magsusulat din kami ng Spring Boot application na may built-in na suporta sa Flyway at magsasagawa ng schema migration habang sine-set up ang konteksto ng application. Kapag gumagamit ng Flyway, maaari kang mag-imbak ng mga script ng paglilipat sa iyong folder ng mga proyekto (bilang default sa classpath:db/migration). Dito makikita mo ang isang halimbawa ng mga naturang migration file

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

Sa halimbawang ito, makikita natin ang 4 na migration script na, kung hindi pa naisakatuparan dati, ay isasagawa nang sunud-sunod kapag nagsimula ang application. Tingnan natin ang isa sa mga file (V1__init.sql) bilang halimbawa.

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

Ang lahat ay ganap na nagpapaliwanag sa sarili: maaari mong gamitin ang SQL upang tukuyin kung paano dapat baguhin ang iyong database. Para sa higit pang impormasyon tungkol sa Spring Boot at Flyway, tingnan Spring Boot Docs.

Sa pamamagitan ng paggamit ng source control tool na may Spring Boot, makakakuha ka ng 2 malaking benepisyo:

  • pinaghihiwalay mo ang mga pagbabago sa database mula sa mga pagbabago sa code
  • Nagaganap ang paglilipat ng database kasama ng paglulunsad ng iyong aplikasyon, ibig sabihin. ang iyong proseso ng pag-deploy ay pinasimple

Pag-troubleshoot ng mga problema sa database

Sa susunod na seksyon ng artikulo, magtutuon kami sa pagtingin sa dalawang diskarte sa mga pagbabago sa database.

  • pabalik na hindi pagkakatugma
  • pabalik na pagkakatugma

Ang una ay ituturing bilang isang babala na hindi ka dapat magsagawa ng zero downtime deployment nang walang paunang paghahanda... Ang pangalawa ay nag-aalok ng solusyon kung paano mo magagawa ang isang deployment nang walang downtime at kasabay nito ay mapanatili ang backward compatibility.

Ang aming proyektong gagawin namin ay isang simpleng Spring Boot Flyway na application na mayroon Person с first_name и last_name sa database (tinatayang pagsasalin: Person ay isang mesa at first_name и last_name - ito ang mga patlang sa loob nito). Gusto naming palitan ang pangalan last_name в surname.

Mga pagpapalagay

Bago tayo pumasok sa mga detalye, may ilang mga pagpapalagay na kailangan nating gawin tungkol sa ating mga aplikasyon. Ang pangunahing resulta na nais nating makamit ay isang medyo simpleng proseso.

Ang tala. Negosyo PRO-TIP. Ang pagpapasimple ng mga proseso ay makakapagtipid sa iyo ng maraming pera sa suporta (sa mas maraming tao na nagtatrabaho ka para sa iyong kumpanya, mas maraming pera ang maaari mong i-save)!

Hindi na kailangang i-rollback ang database

Pinapasimple nito ang proseso ng pag-deploy (halos imposible ang ilang rollback sa database, gaya ng rollback sa pagtanggal). Mas gusto naming i-roll back lang ang mga application. Sa ganitong paraan, kahit na mayroon kang iba't ibang mga database (halimbawa, SQL at NoSQL), magiging pareho ang hitsura ng iyong pipeline ng deployment.

LAGING posible na ibalik ang application ng isang bersyon pabalik (wala na)

Ang rollback ay dapat lamang gawin kung kinakailangan. Kung mayroong isang bug sa kasalukuyang bersyon na hindi madaling maayos, dapat tayong makabalik sa pinakabagong gumaganang bersyon. Ipinapalagay namin na ang pinakabagong gumaganang bersyon na ito ay ang nauna. Ang pagpapanatili ng code at database compatibility para sa higit sa isang rollout ay magiging lubhang mahirap at magastos.

Ang tala. Para sa higit na pagiging madaling mabasa, sa artikulong ito ay babaguhin namin ang pangunahing bersyon ng application.

Hakbang 1: Paunang Estado

Bersyon ng app: 1.0.0
Bersyon ng DB: v1

Puna

Ito ang magiging paunang estado ng aplikasyon.

Mga pagbabago sa database

Naglalaman ang DB 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');

Mga pagbabago sa code

Ang application ay nag-iimbak ng data ng Tao sa 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
                + "]";
    }
}

Paatras na hindi tugmang pagpapalit ng pangalan ng column

Tingnan natin ang isang halimbawa kung paano baguhin ang pangalan ng column:

Pansin Ang sumusunod na halimbawa ay sadyang masira ang mga bagay. Ipinakita namin ito upang ipakita ang problema sa pagiging tugma sa database.

Bersyon ng app: 2.0.0.BAD

Bersyon ng DB: v2bad

Puna

Ang mga kasalukuyang pagbabago ay HINDI nagpapahintulot sa amin na magpatakbo ng dalawang pagkakataon (luma at bago) sa parehong oras. Kaya, ang zero downtime deployment ay magiging mahirap na makamit (kung ang mga pagpapalagay ay isinasaalang-alang, ito ay talagang imposible).

Pagsubok sa A/B

Ang kasalukuyang sitwasyon ay mayroon kaming bersyon ng application 1.0.0, deployed sa produksyon, at database v1. Kailangan nating mag-deploy ng pangalawang pagkakataon ng application, bersyon 2.0.0.BAD, at i-update ang database sa v2bad.

Mga Hakbang:

  1. isang bagong instance ng application na bersyon ay na-deploy 2.0.0.BADna nag-a-update ng database sa v2bad
  2. sa database v2bad haligi last_name hindi na umiiral - ito ay binago sa surname
  3. Ang database at pag-update ng application ay matagumpay at tumatakbo ang ilang mga pagkakataon 1.0.0, iba pa - sa 2.0.0.BAD. Ang lahat ay konektado sa database v2bad
  4. lahat ng pagkakataon ng bersyon 1.0.0 magsisimulang maghagis ng mga error dahil susubukan nilang magpasok ng data sa column last_namena wala na
  5. lahat ng pagkakataon ng bersyon 2.0.0.BAD gagana nang walang problema

Gaya ng nakikita mo, kung gagawa kami ng mga pabalik na hindi tugmang pagbabago sa database at application, imposible ang pagsubok sa A/B.

Rollback ng aplikasyon

Ipagpalagay natin na pagkatapos subukang gawin ang A/B deployment (tinatayang per.: malamang na ang ibig sabihin ng may-akda ay A/B testing dito) napagpasyahan namin na kailangan naming ibalik ang application sa bersyon 1.0.0. Sabihin nating ayaw nating i-rollback ang database.

Mga Hakbang:

  1. itinigil namin ang instance ng application na bersyon 2.0.0.BAD
  2. ang database ay pa rin v2bad
  3. mula noong bersyon 1.0.0 hindi maintindihan kung ano ito surname, makikita natin ang mga error
  4. ang impyerno ay nasira, hindi na tayo makakabalik

Gaya ng nakikita mo, kung gagawa kami ng pabalik na hindi tugmang mga pagbabago sa database at application, hindi kami makakabalik sa nakaraang bersyon.

Mga log ng pagpapatupad ng script

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

Mga pagbabago sa database

Migration script na nagpapalit ng pangalan last_name в surname

Pinagmulan ng script ng 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');

Script na nagpapalit ng pangalan last_name.

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

Mga pagbabago sa code

Binago namin ang pangalan ng field lastName sa surname.

Ang pagpapalit ng pangalan ng column sa backwards-compatible na paraan

Ito ang pinakakaraniwang sitwasyon na maaari nating maranasan. Kailangan nating gumawa ng mga pabalik na hindi tugmang pagbabago. Napatunayan na namin na para sa zero-downtime deployment, hindi namin dapat ilapat ang paglilipat ng database nang walang karagdagang mga hakbang. Sa seksyong ito ng artikulo, magsasagawa kami ng 3 deployment ng application kasama ang mga paglilipat ng database upang makamit ang ninanais na resulta habang pinapanatili ang backward compatibility.

Ang tala. Alalahanin na mayroon kaming database ng bersyon v1. Naglalaman ito ng mga column first_name и last_name. Kailangan nating magbago last_name sa surname. Mayroon din kaming bersyon ng app 1.0.0, na hindi pa ginagamit surname.

Hakbang 2: Magdagdag ng apelyido

Bersyon ng app: 2.0.0
Bersyon ng DB: v2

Puna

Sa pamamagitan ng pagdaragdag ng bagong column at pagkopya ng mga nilalaman nito, lumilikha kami ng pabalik na katugmang mga pagbabago sa database. Kasabay nito, kung i-rollback natin ang JAR o may lumang JAR na tumatakbo, hindi ito masisira sa panahon ng pagpapatupad.

Naglulunsad kami ng bagong bersyon

Mga Hakbang:

  1. magsagawa ng paglilipat ng database upang lumikha ng bagong column surname. Ngayon ang iyong bersyon ng DB v2
  2. kopyahin ang data mula sa last_name в surname. Magbayad ng pansinna kung marami kang data na ito, dapat mong isaalang-alang ang batch migration!
  3. isulat ang code kung saan ginagamit ang mga ito PAREHO и bagoAt ang lumang hanay. Ngayon ang iyong bersyon ng app 2.0.0
  4. basahin ang halaga mula sa column surname, kung hindi null, o mula sa last_namekung surname hindi tinukoy. Maaari mong tanggalin getLastName() mula sa code, dahil maglalabas ito null kapag ibinabalik ang iyong aplikasyon mula sa 3.0.0 sa 2.0.0.

Kung gumagamit ka ng Spring Boot Flyway, ang dalawang hakbang na ito ay isasagawa sa panahon ng pagsisimula ng bersyon 2.0.0 mga aplikasyon. Kung manu-mano mong pinapatakbo ang tool sa pag-bersyon ng database, kakailanganin mong gawin ang dalawang magkaibang bagay para magawa ito (manu-manong i-update ang bersyon ng db at pagkatapos ay i-deploy ang bagong application).

Mahalaga ito. Tandaan na ang bagong likhang column HINDI DAPAT maging HINDI Null. Kung gagawa ka ng rollback, hindi alam ng lumang application ang tungkol sa bagong column at hindi ito mai-install habang tumatagal Insert. Ngunit kung idagdag mo ang hadlang na ito at ang iyong db ay magiging v2, mangangailangan ito ng pagtatakda ng halaga ng bagong column. Na hahantong sa mga paglabag sa mga paghihigpit.

Mahalaga ito. Dapat mong alisin ang pamamaraan getLastName(), dahil sa bersyon 3.0.0 Walang konsepto ng isang column sa code last_name. Nangangahulugan ito na ang null ay itatakda doon. Maaari mong iwanan ang pamamaraan at magdagdag ng mga tseke para sa null, ngunit ang isang mas mahusay na solusyon ay upang matiyak na nasa lohika getSurname() pinili mo ang tamang hindi zero na halaga.

Pagsubok sa A/B

Ang kasalukuyang sitwasyon ay mayroon kaming bersyon ng application 1.0.0, naka-deploy sa produksyon, at ang database sa v1. Kailangan naming mag-deploy ng pangalawang pagkakataon ng application na bersyon 2.0.0na mag-a-update ng database sa v2.

Mga Hakbang:

  1. isang bagong instance ng application na bersyon ay na-deploy 2.0.0na nag-a-update ng database sa v2
  2. pansamantala, ang ilang kahilingan ay naproseso ng mga pagkakataong bersyon 1.0.0
  3. matagumpay ang pag-update at marami kang tumatakbong pagkakataon ng application na bersyon 1.0.0 at iba pang mga bersyon 2.0.0. Nakikipag-ugnayan ang lahat sa database sa v2
  4. bersyon 1.0.0 hindi gumagamit ng column ng apelyido sa database, ngunit ang bersyon 2.0.0 gamit. Hindi sila nakikialam sa isa't isa, at hindi dapat magkaroon ng mga pagkakamali.
  5. bersyon 2.0.0 nag-iimbak ng data sa luma at bagong column, na tinitiyak ang backward compatibility

Mahalaga ito. Kung mayroon kang anumang mga query na nagbibilang ng mga item batay sa mga halaga mula sa luma/bagong column, dapat mong tandaan na mayroon ka na ngayong mga duplicate na halaga (malamang na lumilipat pa rin sila). Halimbawa, kung gusto mong bilangin ang bilang ng mga user na ang apelyido (anuman ang tawag sa column) ay nagsimula sa titik A, pagkatapos hanggang sa makumpleto ang paglipat ng data (oldnew column) maaari kang magkaroon ng hindi tugmang data kung magtatanong ka ng bagong column.

Rollback ng aplikasyon

Ngayon mayroon kaming bersyon ng app 2.0.0 at database sa v2.

Mga Hakbang:

  1. ibalik ang iyong application sa bersyon 1.0.0.
  2. bersyon 1.0.0 ay hindi gumagamit ng column sa database surname, kaya dapat maging matagumpay ang rollback

Mga pagbabago sa DB

Ang database ay naglalaman ng isang column na pinangalanan last_name.

script ng pinagmulan ng 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');

Magdagdag ng script surname.

Pansin Tandaan na HINDI KA MAAARING MAGDAGDAG ng anumang NOT NULL na mga hadlang sa column na iyong idinaragdag. Kung i-rollback mo ang JAR, ang lumang bersyon ay walang ideya tungkol sa idinagdag na column at awtomatikong itatakda ito sa NULL. Kung mayroong ganoong limitasyon, masisira lang ang lumang aplikasyon.

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

Mga pagbabago sa code

Nag-iimbak kami ng data bilang last_name, at sa surname. Sabay-sabay naming binasa mula sa last_name, dahil ang column na ito ang pinakanauugnay. Sa panahon ng proseso ng pag-deploy, maaaring naproseso ang ilang kahilingan ng isang instance ng application na hindi pa naa-update.

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

Hakbang 3: Pag-alis ng last_name mula sa code

Bersyon ng app: 3.0.0

Bersyon ng DB:v3

Puna

Tandaan per.: Tila, sa orihinal na artikulo ay nagkamali ang may-akda ng teksto ng bloke na ito mula sa hakbang 2. Sa hakbang na ito, dapat gawin ang mga pagbabago sa code ng aplikasyon na naglalayong alisin ang functionality na gumagamit ng column last_name.

Sa pamamagitan ng pagdaragdag ng bagong column at pagkopya ng mga nilalaman nito, gumawa kami ng mga pabalik na katugmang pagbabago sa database. Gayundin, kung i-rollback natin ang JAR o may lumang JAR na tumatakbo, hindi ito masisira sa panahon ng pagpapatupad.

Rollback ng aplikasyon

Sa kasalukuyan mayroon kaming bersyon ng app 3.0.0 at database v3. Bersyon 3.0.0 hindi nagse-save ng data sa last_name. Nangangahulugan ito na sa surname ang pinaka-up-to-date na impormasyon ay naka-imbak.

Mga Hakbang:

  1. ibalik ang iyong application sa bersyon 2.0.0.
  2. bersyon 2.0.0 gamit at last_name и surname.
  3. bersyon 2.0.0 Dadalhin surname, kung hindi ito zero, kung hindi -last_name

Mga pagbabago sa database

Walang mga pagbabago sa istruktura sa database. Ang sumusunod na script ay isinasagawa upang maisagawa ang huling paglipat ng lumang data:

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

Mga pagbabago sa code

Tandaan per.: Ang paglalarawan ng block na ito ay nagkamali din na kinopya ng may-akda mula sa hakbang 2. Alinsunod sa lohika ng artikulo, ang mga pagbabago sa code sa hakbang na ito ay dapat na naglalayong alisin mula dito ang mga elemento na gumagana sa column last_name.

Nag-iimbak kami ng data bilang last_name, at sa surname. Bilang karagdagan, nagbabasa kami mula sa kolum last_name, dahil ito ang pinaka-kaugnay. Sa panahon ng proseso ng pag-deploy, maaaring maproseso ang ilang kahilingan ng isang instance na hindi pa naa-upgrade.

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

Hakbang 4: Pag-alis ng last_name mula sa database

Bersyon ng app: 4.0.0

Bersyon ng DB: v4

Puna

Dahil sa ang katunayan na ang code ng bersyon 3.0.0 hindi gumamit ng column last_name, walang masamang mangyayari sa panahon ng execution kung babalik tayo sa 3.0.0 pagkatapos alisin ang isang column mula sa database.

Mga log ng pagpapatupad ng script

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

Mga pagbabago sa DB

Relatibong v3 tinatanggal lang namin yung column last_name at magdagdag ng mga nawawalang paghihigpit.

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

Mga pagbabago sa code

Walang mga pagbabago sa code.

Pagbubuhos

Matagumpay naming nailapat ang pabalik-hindi tugmang pagpapalit ng pangalan ng column sa pamamagitan ng pagsasagawa ng ilang backwards-compatible na deployment. Nasa ibaba ang isang buod ng mga aksyon na ginawa:

  1. deployment ng bersyon ng application 1.0.0 с v1 database schema (pangalan ng column = last_name)
  2. deployment ng bersyon ng application 2.0.0, na nag-iimbak ng data sa last_name и surname. Ang application ay nagbabasa mula sa last_name. Ang database ay nasa bersyon v2naglalaman ng mga column tulad ng last_nameAt surname. surname ay isang kopya ng last_name. (TANDAAN: Ang column na ito ay hindi dapat magkaroon ng not null constraint)
  3. deployment ng bersyon ng application 3.0.0, na nag-iimbak lamang ng data sa surname at nagbabasa mula sa apelyido. Tulad ng para sa database, ang huling paglipat ay nagaganap last_name в surname. Isang limitasyon din HINDI Null tinanggal mula sa last_name. Ang database ay nasa bersyon na ngayon v3
  4. deployment ng bersyon ng application 4.0.0 - walang pagbabagong ginawa sa code. Pag-deploy ng database v4, na nag-aalis last_name. Dito maaari kang magdagdag ng anumang nawawalang mga hadlang sa database.

Sa pamamagitan ng pagsunod sa diskarteng ito, maaari mong palaging i-roll back ang isang bersyon nang hindi sinisira ang pagiging tugma ng database/application.

Kodigo

Ang lahat ng code na ginamit sa artikulong ito ay magagamit sa Github. Nasa ibaba ang karagdagang paglalarawan.

Mga Proyekto

Matapos i-clone ang repositoryo, makikita mo ang sumusunod na istraktura ng folder.

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

Mga script

Maaari mong patakbuhin ang mga script na inilarawan sa mga script sa ibaba, na magpapakita ng mga pabalik na katugma at hindi katugmang mga pagbabago sa database.

Upang makita ang kaso na may mga pabalik na katugmang pagbabago, tumakbo:

./scripts/scenario_backward_compatible.sh

At para makita kaso na may pabalik na hindi tugmang mga pagbabago, tumakbo:

./scripts/scenario_backward_incompatible.sh

Sample ng Spring Boot na Flyway

Ang lahat ng mga halimbawa ay kinuha mula sa Spring Boot Sample Flyway.

Maaari mong tingnan ang http://localhost:8080/flyway, mayroong isang listahan ng mga script.

Kasama rin sa halimbawang ito ang H2 console (sa http://localhost:8080/h2-console) para matingnan mo ang status ng database (default ang jdbc URL ay jdbc:h2:mem:testdb).

Bukod pa rito

Basahin din ang iba pang mga artikulo sa aming blog:

Pinagmulan: www.habr.com

Magdagdag ng komento