Zero Downtime Deployment dhe bazat e të dhënave

Zero Downtime Deployment dhe bazat e të dhënave

Ky this article explains in detail how to resolve database compatibility issues during deployment. We will explain what can happen to your applications in production if you attempt a deployment without prior preparation. Then we will go through the stages of the application lifecycle needed for zero downtime (note: subsequently — zero downtime). The result of our operations will be the application of a back-incompatible database change in a backward-compatible way.

If you want to understand the code examples from the article, you can find them at GitHub.

Hyrje

Zero downtime deployment

What is this mystical zero downtime deployment? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.

How to achieve it? There are several ways, here is one of them:

  • deploy version #1 of your service
  • migrate the DB
  • deploy version #2 of your service alongside version #1
  • as soon as you see that version #2 is working as it should, remove version #1
  • done!

Easy, right? Unfortunately, it's not that simple, and we'll discuss this in detail later. But for now, let's check another fairly common deployment process — blue green deployment.

Have you ever heard of blue green deployment? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на ky artikull, where we describe this in more detail. In short, let’s summarize how to perform blue green deployment:

  • ensure two copies of your production code are running (“blue” and “green”);
  • direct all traffic to the blue environment, i.e., that the production URLs point there;
  • deploy and test all application changes in the green environment;
  • switch the URLs from the blue to the green environment

Blue green deployment is an approach that allows you to easily introduce new features without worrying that production will break. This is due to the fact that even if something goes wrong, you can easily roll back to the previous environment by simply 'flipping a switch'.

After reading all the above, you might ask the question: What does zero downtime have to do with blue green deployment?

Well, they have quite a lot in common, as maintaining two copies of the same environment requires double the effort to manage them. That's why some teams, as stated by Martin Fowler, adhere to a variation of this approach:

një alternativë tjetër është të përdorim të njëjtën bazë të dhënash, duke krijuar kalime blu-të gjelbra për web dhe domain layers. Në këtë qasje, bazat e të dhënave shpesh mund të jenë problematike, veçanërisht kur ju nevojitet të ndryshoni skemën e saj për të mbështetur një version të ri të softuerit.

Këtu kemi arritur në problemin kryesor të këtij artikulli. Baza e të dhënave. Le të shohim përsëri këtë frazë.

kryeni migrimin e bazës së të dhënave.

Tani duhet t'i bëni vetes një pyetje — çfarë, nëse ndryshimi i bazës së të dhënave është prapa jo kompatibel? A do të thyhet versioni im i parë i aplikacionit? Në të vërtetë, kjo do të ndodhte...

Kështu, edhe pse përfitimet e zero downtime / blue green deployment janë të mëdha, kompanitë tendencojnë të ndjekin procesin më të sigurt për të zhvilluar aplikacionet e tyre:

  • preparoni një paketë me versionin e ri të aplikacionit
  • ndaloni aplikacionin e lançuar
  • ekzekutoni skemat për migrimin e bazës së të dhënave
  • zhvilloni dhe lançoni versionin e ri të aplikacionit

Në këtë artikull do të përshkruajmë në detaje se si mund të punoni me bazën e të dhënave dhe kodin, për të shfrytëzuar përfitimet e zero downtime deployment.

Problemet me bazën e të dhënave

Nëse keni një aplikacion stateless që nuk ruan të dhëna në bazën e të dhënave, mund të merrni zero downtime deployment menjëherë. Fatkeqësisht, pjesa më e madhe e softuerit ka nevojë të ruajë të dhëna diku. Kjo është arsyeja pse duhet ta mendoni dy herë para se të bëni ndonjë ndryshim në skemë. Para se të thellohemi në detajet se si të ndryshoni skemën në një mënyrë që të bëhet e mundur zhvillimi pa ndalesa, le të fokusohemi së pari në skemën e menaxhimit të versioneve.

Skema e menaxhimit të versioneve

Në këtë artikull ne do të përdorim Flyway si një mjet për menaxhimin e versioneve (shën. red.: bëhet fjalë për migrimet e bazës së të dhënave). Natyrisht, ne gjithashtu do të shkruajmë një aplikacion Spring Boot, i cili ka mbështetje të integruar për Flyway dhe do të kryejë migrimin e skemës gjatë konfigurimit të kontekstit të aplikacionit. Duke përdorur Flyway, mund të ruani skemat e migrimit në dosjen e projekteve tuaja (në mënyrë të paracaktuar në classpath:db/migration). Këtu mund të shihni një shembull të tillë të skemave të migrimit

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

Në këtë shembull ne shohim 4 skenarë migrimi, të cilët, nëse nuk janë realizuar më parë, do të kryhen një pas një gjatë aktivizimit të aplikacionit. Le të shqyrtojmë një nga skedarët (V1__init.sql) si 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');

Të gjitha flasin qartë për veten: mund të përdorni SQL për të përcaktuar si duhet të ndryshohet baza juaj e të dhënave. Për më shumë informacion mbi Spring Boot dhe Flyway, shikoni Dokumentet e Spring Boot.

Duke përdorur një mjet menaxhimi versioni me Spring Boot, ju fitoni 2 përfitime të mëdha:

  • ju ndan ndryshimet e bazës së të dhënave nga ndryshimet e kodit
  • migrimi i bazës së të dhënave ndodh së bashku me lançimin e aplikacionit tuaj, dmth. procesi juaj i deploy-it thjeshtohet

Zgjidhja e problemeve me bazën e të dhënave

Në seksionin e ardhshëm të artikullit, ne do të përqendrohemi në shqyrtimin e dy qasjeve për ndryshimet në bazën e të dhënave.

  • mośpajtimi kthyes
  • mośpajtimi i kaluar

E para do të shqyrtohet si një paralajmërim, që nuk duhet të realizoni deploy me zero downtime pa përgatitje paraprake… E dyta ofron një zgjidhje se si mund të realizoni deploy pa ndalesa dhe njëkohësisht të mbani mośpajtimin e kaluar.

Projekti ynë, mbi të cilin do të punojmë, do të jetë një aplikacion i thjeshtë Spring Boot Flyway, në të cilin ka Person me first_name dhe last_name në bazën e të dhënave (shënim i përkthyesit: Person është tabela, dhe first_name dhe last_name është fushë në të). Ne duam ta riemërtojmë last_name në surname.

Supozimet

Para se të thellohemi në detaje, është e nevojshme të shënojmë disa supozime lidhur me aplikacionet tona. Rezultati kryesor që dëshirojmë të arrijmë do të jetë një proces mjaft i thjeshtë.

Shënim. Këshillë profesionale. Thjeshtimi i proceseve mund t'ju kursejë shumë para për mbështetje (sa më shumë njerëz të punojnë në kompaninë tuaj, aq më shumë para mund të kurseni)!

Nuk duhet të bëni rikthim të bazës së të dhënave

Kjo e thjeshton procesin e deploy-it (disa rikthime të bazës së të dhënave janë praktikisht të pamundura, për shembull rikthimi i fshirjes). Ne preferojmë të rikthejmë vetëm aplikacionet. Kështu, edhe nëse keni baza të dhënash të ndryshme (për shembull, SQL dhe NoSQL), pipeline-i juaj i deploy-it do të duket njëshe.

Duhet që gjithmonë të ketë mundësi për të rikthyer aplikacionin një version mbrapa (jo më shumë)

Rikthimi duhet të bëhet vetëm në rast nevoje. Nëse versioni aktual ka një gabim që është i vështirë për t'u rregulluar, ne duhet të jemi në gjendje të kthehemi në versionin e fundit funksional. Ne supzojmë se ky version i fundit funksional është versioni paraprak. Mbështetja e kompatibilitetit të kodit dhe të dhënave për më shumë se një version të nxjerrë do të ishte jashtëzakonisht e vështirë dhe e shtrenjtë.

Shënim. Për më shumë lexueshmëri, brenda këtij artikulli ne 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 DB

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

Rinovimi jo-kompatibil i kolonës

Le të shqyrtojmë një shembull se si të ndryshojmë emrin e kolonës:

Kujdes. Shembulli në vazhdim do të çojë qëllimisht në dështim. Ne e tregojmë këtë për të demonstruar problemin e kompatibilitetit të 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 ekzemplarë (të vjetër dhe të ri) në të njëjtën kohë. Kështu, zbatimi pa ndalesa do të jetë i vështirë për t'u arritur (nëse marrim parasysh supozimet, në fakt është e pamundur).

A/B testimi

Situata aktuale është se kemi një aplikacion versioni 1.0.0, të vendosur në prodhim, dhe DB v1. Ne duhet të vendosim një ekzemplar të dytë të aplikacionit, versioni 2.0.0.BAD, dhe të përmirësojmë bazën e të dhënave deri në v2bad.

Hapat:

  1. ka shpërndarë një ekzemplar të ri të aplikacionit versioni 2.0.0.BAD, i cili përmirëson bazën e të dhënave deri në v2bad
  2. në bazën e të dhënave v2bad kolona last_name nuk ekziston më — është ndryshuar në surname
  3. përmirësimi i bazës së të dhënave dhe aplikacionit kaloi me sukses, dhe disa ekzemplarë funksionojnë në 1.0.0, të tjerët — në 2.0.0.BAD. Të gjithë janë të lidhur me DB v2bad
  4. të gjithë ekzemplarët e versionit 1.0.0 do të fillojnë të japin gabime, sepse ata do të përpiqen të futin të dhëna në kolonën last_name, e cila nuk ekziston më
  5. të gjithë ekzemplarët e versionit 2.0.0.BAD do të funksionojnë pa probleme

Siç e shihni, nëse ne bëjmë ndryshime jo-kompatibile në DB dhe aplikacion, A/B testimi është e pamundur.

Rikthimi i aplikacionit

Le të supozojmë se pas përpjekjes për të kryer një zbatim A/B (shënim i përkthyesit: ndoshta këtu autori kishte në mendje A/B testimin) ne vendosëm se na nevojitet të rikthejmë aplikacionin në versionin 1.0.0. Supozoni se nuk duam të bëjmë rikthimin e bazës së të dhënave.

Hapat:

  1. ndalojmë instancën e aplikacionit versioni 2.0.0.BAD
  2. bazën e të dhënave akoma v2bad
  3. sepse versioni 1.0.0 nuk e kupton se çfarë është surname, do të shohim gabime
  4. demonët janë liruar, nuk mund të kthehemi më

Siç e shihni, nëse bëjmë ndryshime të pa kompatibilitetit në DB dhe aplikacion, ne nuk mund të kthehemi në versionin e mëparshëm.

Logët e ekzekutimit të skriptit

Skenari i pa kompatibilitetit:

Ndryshimet e DB

Skripti i migrimit, i cili rinomin last_name në surname

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

Skripti, i cili rinomin last_name.

-- Ky ndryshim është i pa kompatibilitet - nuk mund të bëni testim A/B
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;

Ndryshimet e kodit

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

Rinomin e kolonës në një mënyrë të pa kompatibilitet

Kjo është situata më e zakonshme me të cilën ne mund të përballeshim. Na nevojitet të bëjmë ndryshime të pa kompatibilitet. Ne kemi dëshmuar tashmë se për të kryer një ndarje pa ndërprerje, ne nuk duhet thjesht të aplikojmë migrimin e të dhënave pa veprime shtesë. Në këtë seksion të artikullit do të kryejmë 3 ndarje të aplikacionit së bashku me migrimet e të dhënave, për të arritur rezultatet e dëshiruara dhe gjithashtu për të ruajtur kompatibilitetin e anashtuar.

Shënim. Të kujtojmë se kemi DB versioni v1. Ajo përmban kolona first_name dhe last_name. Ne duhet të ndryshojmë last_name në surname. Po ashtu kemi aplikacion në versionin 1.0.0, i cili akoma nuk e përdor surname.

Hapi 2: Shto surname

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ë pa kompatibilitetit të DB. Në të njëjtën kohë, nëse kthejmë JAR-in ose kemi një JAR të vjetër që punon, ai nuk do të dështojë gjatë ekzekutimit.

Nxjerrim versionin e ri

Hapat:

  1. kryeni migrimin e DB për të krijuar kolonën e re surname. Tani DB juaj i versionit v2
  2. kopjoni të dhënat nga last_name në surname. Vini re, çka nëse keni shumë nga këto të dhëna, duhet të merrni në konsideratë migrimin me paketë!
  3. shkruani kodin, ku përdoren TË DY dhe i ri, dhe të vjetra kolona. Tani tani aplikacioni i juaj version 2.0.0
  4. lexoni vlerën nga kolona surname, nëse ajo nuk null, apo nga last_name, nëse surname nuk është caktuar. Ju mund të hiqni getLastName() nga kodi, pasi ai do të japë null në rastin e rikthimit të aplikacionit tuaj nga 3.0.0 në 2.0.0.

Nëse po përdorni Spring Boot Flyway, këto dy hapa do të realizohen gjatë nisjes së versionit 2.0.0 të aplikacionit. Nëse e nisni mjetin e menaxhimit të versioneve të bazës së të dhënave manualisht, do t'ju duhet të bëni dy veprime të ndryshme për këtë (së pari përditësoni manualisht versionin e db-së dhe pastaj implementoni aplikacionin e ri).

E rëndësishme. Mos harroni se kolona e re e krijuar NUK DUHET të jesh NOT NULL. Nëse bëni rikthim, aplikacioni i vjetër nuk di për kolonën e re dhe nuk do ta vendosë atë gjatë Insert. Por nëse e shtoni këtë kufizim, dhe DB-ja juaj do të v2, do të kërkojë caktimin e vlerës së kolonës së re. Kjo do të çojë në shkelje të kufizimeve.

E rëndësishme. Duhet të hiqni metodën getLastName(), pasi në versionin 3.0.0 në kod nuk ka konceptin e kolonës last_name. Kjo do të thotë që atje do të vendosen null. Ju mund ta lini metodën dhe të shtoni kontrolle për null, por një zgjidhje më e mirë do të ishte të siguroni që në logjikën getSurname() kur zgjidhni vlerën e duhur të paprikë.

A/B testimi

Situata aktuale është se kemi një aplikacion versioni 1.0.0, e implementuar në prodhim, dhe DB-ja në v1. Ne duhet të implementojmë një ekzemplar të dytë të versionit të aplikacionit 2.0.0, i cili do të përditësojë bazën e të dhënave deri në v2.

Hapat:

  1. ka shpërndarë një ekzemplar të ri të aplikacionit versioni 2.0.0, i cili përmirëson bazën e të dhënave deri në v2
  2. ndërkohë disa kërkesa u përpunuan nga ekzemplarët e versionit 1.0.0
  3. përditësimi kaloi me sukses, dhe keni disa ekzemplarë funksionalë të aplikacionit të versionit 1.0.0 dhe versionet e tjera 2.0.0. Të gjitha komunikojnë me DB-në në v2
  4. i 1.0.0 nuk përdor në DB kolonën e mbiemrit, ndërsa versioni 2.0.0 e përdor. Ato nuk e pengojnë njëra-tjetrën, dhe nuk duhet të ketë gabime.
  5. i 2.0.0 ruan të dhënat si në kolonën e vjetër ashtu edhe në atë të re, duke siguruar kështu përputhshmërinë prapavepruese

E rëndësishme. Nëse keni ndonjë kërkesë që numëron elementet në bazë të vlerave nga kolona e vjetër / e re, duhet të mbani mend se tani keni dyfishim të vlerave (më shumë se një herë mund të jenë ende në migrim). Për shembull, nëse dëshironi të llogaritni numrin e përdoruesve, emri i mbiemrit (pavarësisht si e quani kolonën) fillonte me shkronjën A, atëherë deri në përfundimin e migrimit të të dhënave (old → të reja kolona) mund të keni të dhëna jo konsistente, nëse ekzekutoni një kërkesë në kolonën e re.

Rikthimi i aplikacionit

Tani kemi aplikacionin e versionit 2.0.0 dhe bazën e të dhënave në v2.

Hapat:

  1. ktheni aplikacionin tuaj në versionin 1.0.0.
  2. i 1.0.0 nuk përdor në DB kolonën surname, prandaj rikthimi duhet të jetë i suksesshëm

Ndryshimet DB

DB përmban një kolonë me emrin last_name.

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

Skedari i shtimit surname.

Kujdes. Kujtoni se nuk DUHET të SHTOhen asnjë kufizim NOT NULL në kolonën e shtuar. Nëse e riktheni JAR-in, versioni i vjetër nuk do të ketë njohuri për kolonën e shtuar dhe do t'i caktojë automatikisht vlerën NULL. Nëse ka një kufizim të tillë, aplikacioni i vjetër do të prishet.

-- VËREJTJE: Ky fushë nuk mund të ketë kufizimin NOT NULL sepse nëse e riktheni, versioni i vjetër nuk do të dijë për këtë fushë
-- dhe do ta caktojë gjithmonë në NULL
ALTER TABLE PERSON ADD surname varchar(255);

-- NE PO SUPOZONIMI QË ËSHTË NJË MIGRIM I SHPEJTË - ND otherwise DO TË DUHEJ TË MIGRONIM NË GRUPE
UPDATE PERSON SET PERSON.surname = PERSON.last_name

Ndryshimet e kodit

Ne ruajmë të dhënat si në last_name, ashtu edhe në surname. Ndërkohë lexojmë nga last_name, sepse kjo kolonë është më e rëndësishmja. Gjatë procesit të shpërndarjes, disa pyetje mund të jenë trajtuar nga një instancë 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 last_name nga kodi

Versioni i aplikacionit: 3.0.0

Versioni i DB:v3

Koment

Shënim: Duket se në artikullin origjinal autori gabimisht kopjoi tekstin e këtij blloku nga hapi 2. Në këtë hap, duhet bërë ndryshime në kodin e aplikacionit për të hequr funksionalitetin që përdor kolonën last_name.

Duke shtuar një kolonë të re dhe duke kopjuar përmbajtjen e saj, krijuam ndryshime të përputhshme me versionet e mëparshme të DB. Gjithashtu, nëse e rikthejmë JAR-in ose kemi një JAR të vjetër që funksionon, ai nuk do të prishet gjatë ekzekutimit.

Rikthimi i aplikacionit

Aktualisht kemi një aplikacion versioni 3.0.0 dhe një bazë të dhënash v3. Versioni 3.0.0 nuk ruan të dhënat në last_name. Kjo do të thotë se në surname ruhet informacioni më i fundit.

Hapat:

  1. ktheni aplikacionin tuaj në versionin 2.0.0.
  2. i 2.0.0 përdor dhe last_name dhe surname.
  3. i 2.0.0 do të marrë surname, nëse ai nuk është NULL, përndryshe —last_name

Ndryshimet e DB

Nuk ka ndryshime strukturore në DB. Po ekzekutohet skedari i mëposhtëm që bën migrimin përfundimtar të të dhënave të vjetra:

-- NE PO SUPOZONIMI QË ËSHTË NJË MIGRIM I SHPEJTË - NË RASHT SE KJO DO TË DUHET TË MIGRONIM NË GRUPE
-- PO ASNJËHERË NUK JEMI TË SIGURT NËSE NUK JEMI DUKE KALUAR HULLORE DHE PËRMBAJTJE EKZISTUESE. DUHET TË KOMPAROJMË
-- VERSIONE TË HULLORE TË SIGURIMI QË NËSE KA NJË HULLOR ME NJË NUMËR VERSIONI MË TË LARTË
-- NE NUK DO TË KALUARIMË
UPDATE PERSON SET PERSON.surname = PERSON.last_name;

-- HEQJA E KUFIZIMIT NOT NULL; PËNDYSH SE DO TË PROVOJMË TË SHTOJMË VLERË NULL TË LAST_NAME
-- ME NJË KUFIZIM NOT NULL.
ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;

Ndryshimet e kodit

Shënim: Përshkrimi i këtij blloku gjithashtu u kopjua gabimisht nga autori nga hapi 2. Sipas logjikës së tregimit të artikullit, ndryshimet në kodin në këtë hap duhet të jenë të orientuara për të hequr elementët që punojnë me kolonën last_name.

Ne ruajmë të dhënat si në last_name, ashtu edhe në surname. Për më tepër, ne lexojmë nga kolona last_name, pasi është më relevante. Gjatë procesit të shpërndarjes, disa kërkesa mund të trajtohen nga një instancë që ende nuk është azhurnuar.

/*
 * 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: Fshirja e last_name nga DB

Versioni i aplikacionit: 4.0.0

Versioni i DB: v4

Koment

Për shkak se kodi version 3.0.0 nuk përdori kolonën last_name, gjatë ekzekutimit nuk do të ndodhë asgjë e keqe nëse rikthemi në 3.0.0 pas fshirjes së kolonës nga baza e të dhënave.

Logët e ekzekutimit të skriptit

Ne do ta bëjmë në mënyrën e mëposhtme:

01) Ekzekuto 1.0.0
02) Prisni që aplikacioni (1.0.0) të boot-ojë
03) Gjeneroni një person duke thirrur POST localhost:9991/person për versionin 1.0.0
04) Ekzekuto 2.0.0
05) Prisni që aplikacioni (2.0.0) të boot-ojë
06) Gjeneroni një person duke thirrur POST localhost:9991/person për versionin 1.0.0
07) Gjeneroni një person duke thirrur POST localhost:9992/person për versionin 2.0.0
08) Vrit aplikacionin (1.0.0)
09) Ekzekuto 3.0.0
10) Prisni që aplikacioni (3.0.0) të boot-ojë
11) Gjeneroni një person duke thirrur POST localhost:9992/person për versionin 2.0.0
12) Gjeneroni një person duke thirrur POST localhost:9993/person për versionin 3.0.0
13) Vrit aplikacionin (3.0.0)
14) Ekzekuto 4.0.0
15) Prisni që aplikacioni (4.0.0) të boot-ojë
16) Gjeneroni një person duke thirrur POST localhost:9993/person për versionin 3.0.0
17) Gjeneroni një person duke thirrur POST localhost:9994/person për versionin 4.0.0

Duke nisur aplikacionin në versionin 1.0.0
Gjeneroni një person në versionin 1.0.0
Dërgimi i një postimi në 127.0.0.1:9991/person. Kjo është përgjigjja:

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

Duke nisur aplikacionin në versionin 2.0.0

Gjeneroni një person në versionin 1.0.0
Dërgimi i një postimi në 127.0.0.1:9991/person. Kjo është përgjigjja:

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

Gjeneroni një person në versionin 2.0.0
Dërgimi i një postimi në 127.0.0.1:9992/person. Kjo është përgjigjja:

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

Duke vrarë aplikacionin 1.0.0

Duke nisur aplikacionin në versionin 3.0.0

Gjeneroni një person në versionin 2.0.0
Dërgimi i një postimi në 127.0.0.1:9992/person. Kjo është përgjigjja:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}

Gjeneroni një person në versionin 3.0.0
Dërgimi i një postimi në 127.0.0.1:9993/person. Kjo është përgjigjja:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}

Duke vrarë aplikacionin 2.0.0

Duke nisur aplikacionin në versionin 4.0.0

Gjeneroni një person në versionin 3.0.0
Dërgimi i një postimi në 127.0.0.1:9993/person. Kjo është përgjigjja:

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

Gjeneroni një person në versionin 4.0.0
Dërgimi i një postimi në 127.0.0.1:9994/person. Kjo është përgjigjja:

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

Ndryshimet e DB

Sa i përket v3 ne thjesht po fshijmë kolonën last_name dhe po shtojmë kufizimet e munguar.

-- FSHI KOLONËN
ALTER TABLE PERSON DROP last_name;

-- SHTO KUFIZIMET
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.

Përfundimi

Ne e aplikuam me sukses ndryshimin e paqartë të emrit të kolonës duke realizuar disa shpërndarje të kthyeshme. Më poshtë është një përmbledhje e veprimeve të kryera:

  1. shpërndarjen e aplikacionit në versionin 1.0.0 me v1 skemat e DB (emri i kolonës = last_name)
  2. shpërndarjen e aplikacionit në versionin 2.0.0, e cila ruan të dhënat në last_name dhe surname. Aplikacioni lexon nga last_name. Baza e të dhënave ndodhet në versionin v2, duke përfshirë kolonat si last_name, ashtu edhe surname. surname është një kopje e last_name. (VËREJTJE: kjo kolonë nuk duhet të ketë kufizimin not null)
  3. shpërndarjen e aplikacionit në versionin 3.0.0, që ruan të dhëna vetëm në surname dhe lexon nga surname. Sa i përket Bazës së të Dhënave, bëhet migrimi i fundit last_name në surname. Po ashtu, kufizimi NOT NULL shkëlqet nga last_name. Baza e të dhënave tani është në versionin v3
  4. shpërndarjen e aplikacionit në versionin 4.0.0 — nuk bëhen ndryshime në kod. Depoimi i bazës së të dhënave v4, e cila fshin last_name. Këtu mund të shtoni çdo kufizim të humbur në Bazën e të Dhënave.

Duke ndjekur këtë qasje, mund të riktheheni gjithmonë një version më parë, pa prishur kompatibilitetin e Bazës së të Dhënave / aplikacionit.

Kodi

I gjithë kodi i përdorur në këtë artikull është në dispozicion në Github. Më poshtë përshkrimi shtesë.

Projekte

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

├── boot-flyway-v1              - versioni 1.0.0 i aplikacionit me v1 të skemës
├── boot-flyway-v2              - versioni 2.0.0 i aplikacionit me v2 të skemës (kompatibil prapa - aplikacioni mund të rikthehet)
├── boot-flyway-v2-bad          - versioni 2.0.0.BAD i aplikacionit me v2bad të skemës (inkompatibil prapa - aplikacioni nuk mund të rikthehet)
├── boot-flyway-v3              - versioni 3.0.0 i aplikacionit me v3 të skemës (aplikacioni mund të rikthehet)
└── boot-flyway-v4              - versioni 4.0.0 i aplikacionit me v4 të skemës (aplikacioni mund të rikthehet)

Skripte

Mund të ekzekutoni skenarët e përshkruar në skriptet më poshtë, të cilët demonstrojnë ndryshimet e përputhshme dhe të papërputhshme në Bazën e të Dhënave.

Për të parë rastin me ndryshimet e përputhshme, ekzekutoni:

.\/scripts\/scenario_backward_compatible.sh

Dhe për të parë rastin me ndryshimet e papërputhshme, ekzekutoni:

.\/scripts\/scenario_backward_incompatible.sh

Shembulli i Spring Boot Flyway

Të gjithë shembujt janë marrë nga Shembulli i Spring Boot Flyway.

Mund të shikoni në http://localhost:8080/flyway, aty është lista e skripteve.

Ky shembull gjithashtu përfshin konsolën H2 (në adresën http://localhost:8080/h2-console), në mënyrë që të mund të shqyrtoni gjendjen e Bazës së të Dhënave (URL jdbc në mënyrë të paracaktuar — jdbc:h2:mem:testdb).

Shtesë

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

Burimi: habr.com

Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS 🔥 Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS | ProHoster