Нөлдік тоқтау уақытын орналастыру және дерекқорлар

Нөлдік тоқтау уақытын орналастыру және дерекқорлар

Бұл мақала қолдану кезінде дерекқордың үйлесімділік мәселелерін шешу жолын егжей-тегжейлі түсіндіреді. Алдын ала дайындықсыз орналастыруға әрекеттенсеңіз, өндірістік қолданбаларға не болуы мүмкін екенін айтамыз. Содан кейін біз нөлдік тоқтау уақытын талап ететін қолданбаның өмірлік циклінің кезеңдерін өтеміз (шамамен. жолақ: ары қарай - нөлдік тоқтау). Біздің әрекеттеріміздің нәтижесі кері сәйкес келмейтін дерекқор өзгертуін кері үйлесімді түрде қолдану болады.

Мақаладағы код мысалдарын түсінгіңіз келсе, оларды мына жерден таба аласыз GitHub.

Кіріспе

Нөлдік тоқтау уақытын орналастыру

Қандай мистикалық нөлдік тоқтау уақытын орналастыру? Сіз бұл қолданбаны қолданушы оның қолжетімсіздігін байқамай тұрғанда, қолданбаның жаңа нұсқасын өндіріске сәтті енгізе алатындай етіп орналастырылған кезде айта аласыз. Пайдаланушы мен компания тұрғысынан бұл ең жақсы мүмкін болатын орналастыру сценарийі, себебі ол жаңа мүмкіндіктерді енгізуге және қателерді үзіліссіз түзетуге мүмкіндік береді.

Бұған қалай қол жеткізуге болады? Бірнеше жол бар, олардың бірі мынада:

  • қызметіңіздің №1 нұсқасын қолданыңыз
  • дерекқорды тасымалдауды орындаңыз
  • Қызметіңіздің №2 нұсқасын №1 нұсқамен қатар орналастырыңыз
  • №2 нұсқаның дұрыс жұмыс істейтінін көргенде, №1 нұсқаны алып тастаңыз
  • дайын!

Оңай, солай емес пе? Өкінішке орай, бұл оңай емес, біз мұны кейінірек егжей-тегжейлі қарастырамыз. Енді тағы бір кең таралған орналастыру процесін - көк жасыл орналастыруды тексерейік.

Сіз естіген жоқсыз ба көк жасыл орналастыру? Cloud Foundry мұны өте оңай етеді. Қараңызшы Бұл мақала, мұнда біз мұны толығырақ сипаттаймыз. Қысқаша қорытындылау үшін көк жасыл орналастыруды қалай жасау керектігін еске салайық:

  • өндірістік кодтың екі данасы («көк» және «жасыл») жұмыс істейтініне көз жеткізіңіз;
  • барлық трафикті көгілдір ортаға бағыттаңыз, яғни. өндірістік URL мекенжайлары сол жерде көрсетілуі үшін;
  • жасыл ортада қолданбаның барлық өзгерістерін орналастыру және тексеру;
  • URL мекенжайларын көктен жасыл ортаға ауыстыру

Көк жасылды орналастыру - өндірістің бұзылуы туралы алаңдамай, жаңа мүмкіндіктерді оңай енгізуге мүмкіндік беретін тәсіл. Бұл бірдеңе болса да, жай ғана «қосқышты түрту» арқылы бұрынғы ортаға оңай оралуға болатындығына байланысты.

Жоғарыда айтылғандардың барлығын оқығаннан кейін, сіз сұрақ қоюыңыз мүмкін: Көк жасыл орналастыруға нөлдік тоқтап қалудың қандай қатысы бар?

Олардың көп ортақ тұстары бар, өйткені бір ортаның екі көшірмесін сақтау оларды сақтау үшін екі есе көп күш жұмсауды талап етеді. Сондықтан кейбір командалар талап етеді Мартин Фаулер, осы тәсілдің нұсқасын орындаңыз:

Тағы бір нұсқа - веб және домен қабаттары үшін көк-жасыл қосқыштарды жасай отырып, бірдей дерекқорды пайдалану. Бұл тәсілде дерекқор жиі қиындық тудыруы мүмкін, әсіресе бағдарламалық жасақтаманың жаңа нұсқасын қолдау үшін оның схемасын өзгерту қажет болғанда.

Міне, біз осы мақаланың негізгі мәселесіне келеміз. Мәліметтер базасы. Осы сөз тіркесіне тағы бір назар аударайық.

дерекқорды тасымалдауды орындаңыз.

Енді сіз өзіңізге сұрақ қоюыңыз керек - егер дерекқорды өзгерту кері үйлесімді болмаса ше? Қолданбаның бірінші нұсқасы бұзылмай ма? Шындығында дәл солай болады...

Сонымен, нөлдік үзіліс/көк жасыл орналастырудың үлкен артықшылықтарына қарамастан, компаниялар өздерінің қосымшаларын орналастыру үшін келесі қауіпсіз процесті ұстануға бейім:

  • қолданбаның жаңа нұсқасы бар пакетті дайындаңыз
  • жұмыс істеп тұрған қолданбаны өшіріңіз
  • дерекқорды тасымалдау үшін сценарийлерді іске қосыңыз
  • қолданбаның жаңа нұсқасын орнатыңыз және іске қосыңыз

Бұл мақалада біз нөлдік тоқтау уақытын орналастыру мүмкіндігін пайдалану үшін дерекқормен және кодпен қалай жұмыс істеуге болатынын егжей-тегжейлі қарастырамыз.

Мәліметтер базасының мәселелері

Егер дерекқорда ешбір деректерді сақтамайтын азаматтығы жоқ қолданба болса, сіз бірден нөлдік тоқтау уақытын қолдана аласыз. Өкінішке орай, көптеген бағдарламалық жасақтама деректерді бір жерде сақтауы керек. Сондықтан схемаға қандай да бір өзгерістер енгізбес бұрын екі рет ойлану керек. Тоқтаусыз орналастыру мүмкін болатындай етіп схеманы өзгерту жолының егжей-тегжейлерімен таныспас бұрын, алдымен нұсқалау схемасына тоқталайық.

Нұсқалау схемасы

Бұл мақалада біз қолданамыз Ұшу жолы нұсқаны басқару құралы ретінде (шамамен. Аударма: біз дерекқорды тасымалдау туралы айтып отырмыз). Әрине, біз сондай-ақ кірістірілген Flyway қолдауы бар Spring Boot қолданбасын жазамыз және қолданба контекстін орнату кезінде схеманы тасымалдауды орындаймыз. Flyway қолданбасын пайдаланған кезде, тасымалдау сценарийлерін жобалар қалтасында сақтауға болады (әдепкі бойынша classpath:db/migration). Мұнда сіз осындай тасымалдау файлдарының мысалын көре аласыз

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

Бұл мысалда біз 4 көшіру сценарийін көреміз, олар бұрын орындалмаса, қолданба іске қосылғанда бірінен соң бірі орындалады. Файлдардың бірін қарастырайық (V1__init.sql) мысал ретінде.

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

Барлығы өте түсінікті: дерекқорды қалай өзгерту керектігін анықтау үшін SQL тілін пайдалануға болады. Spring Boot және Flyway туралы қосымша ақпарат алу үшін тексеріңіз Spring Boot құжаттары.

Spring Boot көмегімен бастапқы басқару құралын пайдалану арқылы сіз 2 үлкен артықшылыққа ие боласыз:

  • дерекқор өзгерістерін код өзгерістерінен бөлесіз
  • Дерекқорды тасымалдау қолданбаны шығарумен бірге жүреді, яғни. орналастыру процессіңіз жеңілдетілді

Мәліметтер қорындағы ақаулықтарды жою

Мақаланың келесі бөлімінде біз дерекқорды өзгертудің екі тәсілін қарастырамыз.

  • кері сәйкессіздік
  • кері үйлесімділік

Біріншісі алдын ала дайындықсыз нөлдік тоқтау уақытын орналастыруды орындамау керектігі туралы ескерту ретінде қарастырылады... Екіншісі орналастыруды тоқтаусыз орындау және сонымен бірге кері үйлесімділікті сақтау туралы шешімді ұсынады.

Біз жұмыс істейтін жобамыз қарапайым Spring Boot Flyway қолданбасы болады Person с first_name и last_name деректер базасында (шамамен. аударма: Person кесте және first_name и last_name - бұл ондағы өрістер). Біз атын өзгерткіміз келеді last_name в surname.

Болжамдар

Егжей-тегжейлерге тоқталмас бұрын, қолданбаларымыз туралы бірнеше болжам жасауымыз керек. Біз қол жеткізгіміз келетін негізгі нәтиже өте қарапайым процесс болады.

Жазба. Бизнес PRO-TIP. Процестерді жеңілдету сізге қолдау көрсетуге көп ақша үнемдей алады (компанияңызда неғұрлым көп адам жұмыс істесе, соғұрлым көп ақша үнемдей аласыз)!

Дерекқорды кері қайтарудың қажеті жоқ

Бұл орналастыру процесін жеңілдетеді (кейбір дерекқорды кері қайтару мүмкін емес, мысалы, жоюды кері қайтару). Біз тек қолданбаларды кері қайтаруды жөн көреміз. Осылайша, әртүрлі дерекқорларыңыз болса да (мысалы, SQL және NoSQL), орналастыру құбыры бірдей көрінеді.

Қолданбаның бір нұсқасын артқа айналдыру ӘРҚАШАН мүмкін болуы керек (артық емес)

Кері қайтару қажет болған жағдайда ғана жасалуы керек. Ағымдағы нұсқада оңай түзетілмейтін қате болса, біз соңғы жұмыс нұсқасына оралуымыз керек. Бұл соңғы жұмыс нұсқасы алдыңғы нұсқа деп есептейміз. Бірден көп шығару үшін код пен дерекқор үйлесімділігін сақтау өте қиын және қымбат болар еді.

Жазба. Жақсырақ оқылу үшін осы мақалада біз қолданбаның негізгі нұсқасын өзгертеміз.

1-қадам: Бастапқы күй

Қолданба нұсқасы: 1.0.0
ДБ нұсқасы: v1

Пікір

Бұл қолданбаның бастапқы күйі болады.

Мәліметтер базасының өзгеруі

ДҚ құрамында 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');

Код өзгерістері

Қолданба адам деректерін сақтайды 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
                + "]";
    }
}

Кері үйлесімсіз баған атауын өзгерту

Баған атауын өзгертудің мысалын қарастырайық:

Назар аударыңыз. Келесі мысал заттарды әдейі бұзады. Біз мұны дерекқор үйлесімділігі мәселесін көрсету үшін көрсетеміз.

Қолданба нұсқасы: 2.0.0.BAD

ДБ нұсқасы: v2bad

Пікір

Ағымдағы өзгертулер бір уақытта екі дананы (ескі және жаңа) іске қосуға мүмкіндік бермейді. Осылайша, нөлдік тоқтау уақытын орналастыруға қол жеткізу қиын болады (егер болжамдар ескерілсе, бұл іс жүзінде мүмкін емес).

A/B сынағы

Қазіргі жағдай бізде қолданбаның нұсқасы бар 1.0.0, өндірісте және деректер базасында орналастырылған v1. Қолданбаның екінші данасын, нұсқасын орналастыруымыз керек 2.0.0.BAD, және дерекқорды келесіге жаңартыңыз v2bad.

Қадамдар:

  1. нұсқа қолданбасының жаңа данасы орналастырылды 2.0.0.BADдерекқорды жаңартады v2bad
  2. мәліметтер базасында v2bad баған last_name енді жоқ – деп өзгертілді surname
  3. Дерекқор мен қолданбаны жаңарту сәтті болды және кейбір даналар іске қосылуда 1.0.0, басқалары - в 2.0.0.BAD. Барлығы дерекқорға қосылған v2bad
  4. нұсқаның барлық даналары 1.0.0 қателер жібере бастайды, себебі олар деректерді бағанға кірістіруге тырысады last_nameенді кім жоқ
  5. нұсқаның барлық даналары 2.0.0.BAD еш қиындықсыз жұмыс істейтін болады

Көріп отырғаныңыздай, егер дерекқорға және қолданбаға кері үйлесімсіз өзгерістер енгізсек, A/B тестілеу мүмкін емес.

Қолданбаны кері қайтару

A/B орналастыруды орындауға тырысқаннан кейін (шамамен. per.: автор бұл жерде A/B тестілеуін білдірген болуы мүмкін) қолданбаны нұсқаға қайтару керек деп шештік 1.0.0. Дерекқорды кері қайтарғымыз келмейді делік.

Қадамдар:

  1. нұсқа қолданбасының данасын тоқтатамыз 2.0.0.BAD
  2. деректер базасы әлі де бар v2bad
  3. нұсқасынан бері 1.0.0 оның не екенін түсінбейді surname, біз қателерді көреміз
  4. тозақ бұзылды, біз енді қайтып орала алмаймыз

Көріп отырғаныңыздай, дерекқорға және қолданбаға кері үйлесімсіз өзгерістер енгізсек, біз алдыңғы нұсқаға орала алмаймыз.

Сценарийді орындау журналдары

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

Мәліметтер базасының өзгеруі

Атын өзгертетін тасымалдау сценарийі last_name в surname

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

Атын өзгертетін сценарий last_name.

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

Код өзгерістері

Біз өріс атауын өзгерттік lastName туралы surname.

Бағанның атын кері үйлесімді жолмен өзгерту

Бұл біз жиі кездесетін жағдай. Біз кері үйлеспейтін өзгерістер енгізуіміз керек. Біз нөлдік тоқтау уақытын қолдану үшін қосымша қадамдарсыз дерекқорды тасымалдауды жай ғана қолданбау керектігін дәлелдедік. Мақаланың осы бөлімінде кері үйлесімділікті сақтай отырып, қалаған нәтижеге қол жеткізу үшін дерекқорды тасымалдаумен бірге қолданбаны 3 орналастыруды орындаймыз.

Жазба. Еске салайық, бізде нұсқалар базасы бар v1. Оның құрамында бағандар бар first_name и last_name. Біз өзгеруіміз керек last_name туралы surname. Бізде қолданба нұсқасы да бар 1.0.0, ол әлі қолданылмаған surname.

2-қадам: тегін қосыңыз

Қолданба нұсқасы: 2.0.0
ДБ нұсқасы: v2

Пікір

Жаңа бағанды ​​қосу және оның мазмұнын көшіру арқылы біз кері үйлесімді дерекқор өзгерістерін жасаймыз. Сонымен қатар, егер JAR кері қайтарылса немесе ескі JAR жұмыс істеп тұрса, ол орындау кезінде үзілмейді.

Біз жаңа нұсқаны шығарамыз

Қадамдар:

  1. жаңа баған жасау үшін дерекқорды тасымалдауды орындаңыз surname. Енді сіздің DB нұсқасы v2
  2. деректерін көшіру last_name в surname. назар аударыңызЕгер сізде бұл деректер көп болса, пакеттік көшіруді қарастыру керек!
  3. олар қолданылатын кодты жазыңыз ЕКІ и новыймен ескі баған. Енді сіздің қолданба нұсқасы 2.0.0
  4. бағандағы мәнді оқыңыз surname, егер олай болмаса null, немесе last_name, егер болса surname белгілі емес. Жоюға болады getLastName() кодтан, себебі ол шығады null қолданбаңызды қайтару кезінде 3.0.0 қарай 2.0.0.

Spring Boot Flyway пайдалансаңыз, бұл екі қадам нұсқаны іске қосу кезінде орындалады 2.0.0 қолданбалар. Дерекқор нұсқасын жасау құралын қолмен іске қоссаңыз, мұны істеу үшін екі түрлі әрекетті орындау керек болады (алдымен db нұсқасын қолмен жаңартыңыз, содан кейін жаңа қолданбаны қолданыңыз).

Бұл өте маңызды. Жаңадан жасалған бағанды ​​есте сақтаңыз МІНДЕТТІ ЕМЕС болуы ЖОҚ. Кері қайтаруды жасасаңыз, ескі қолданба жаңа баған туралы білмейді және оны орнату кезінде орнатпайды Insert. Бірақ егер сіз осы шектеуді қоссаңыз және сіздің db болады v2, бұл жаңа бағанның мәнін орнатуды талап етеді. Бұл шектеулердің бұзылуына әкеледі.

Бұл өте маңызды. Сіз әдісті алып тастауыңыз керек getLastName(), себебі нұсқада 3.0.0 Кодта баған ұғымы жоқ last_name. Бұл онда нөл орнатылатынын білдіреді. Әдісті қалдырып, тексерулерді қосуға болады null, бірақ логикада екеніне көз жеткізу әлдеқайда жақсы шешім болар еді getSurname() нөлден басқа дұрыс мәнді таңдадыңыз.

A/B сынағы

Қазіргі жағдай бізде қолданбаның нұсқасы бар 1.0.0, өндірісте орналастырылған және дерекқор v1. Нұсқа қолданбасының екінші данасын қолдануымыз керек 2.0.0ол дерекқорды жаңартады v2.

Қадамдар:

  1. нұсқа қолданбасының жаңа данасы орналастырылды 2.0.0дерекқорды жаңартады v2
  2. әзірше кейбір сұраулар нұсқа даналары арқылы өңделді 1.0.0
  3. жаңарту сәтті болды және сізде нұсқа қолданбасының бірнеше іске қосылған даналары бар 1.0.0 және басқа нұсқалар 2.0.0. Барлығы дерекқормен байланысады v2
  4. нұсқасы 1.0.0 дерекқордағы фамилия бағанын емес, нұсқасын пайдаланады 2.0.0 пайдаланады. Олар бір-біріне кедергі жасамайды және қателер болмауы керек.
  5. нұсқасы 2.0.0 деректерді ескі және жаңа бағанда сақтай отырып, кері үйлесімділікті қамтамасыз етеді

Бұл өте маңызды. Ескі/жаңа бағандағы мәндерге негізделген элементтерді санайтын кез келген сұрауларыңыз болса, сізде қазір қайталанатын мәндер бар екенін есте ұстаған жөн (олар әлі де тасымалдануда). Мысалы, тегі (баған қалай аталса да) әріптен басталған пайдаланушылардың санын санағыңыз келсе A, содан кейін деректерді тасымалдау аяқталғанша (oldnew баған) жаңа бағанды ​​сұрасаңыз, деректеріңіз сәйкес келмеуі мүмкін.

Қолданбаны кері қайтару

Енді бізде қолданба нұсқасы бар 2.0.0 және деректер базасы v2.

Қадамдар:

  1. қолданбаны нұсқаға қайтарыңыз 1.0.0.
  2. нұсқасы 1.0.0 дерекқордағы бағанды ​​пайдаланбайды surname, сондықтан кері қайтару сәтті болуы керек

ДҚ өзгереді

Дерекқорда аталған баған бар last_name.

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

Сценарий қосыңыз surname.

Назар аударыңыз. Сіз қосып жатқан бағанға ешқандай ЕМЕС NULL шектеулерін ҚОСУ БОЛМАЙТЫНыңызды есте сақтаңыз. Егер JAR кері қайтарылса, ескі нұсқада қосылған баған туралы түсінік болмайды және оны автоматты түрде NULL мәніне орнатады. Егер мұндай шектеу болса, ескі қолданба жай ғана бұзылады.

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

Код өзгерістері

Біз деректерді сақтаймыз last_name, және surname. Сонымен бірге біз одан оқимыз 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;
    private String surname;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * Reading from the new column if it's set. If not the from the old one.
     *
     * When migrating from version 1.0.0 -> 2.0.0 this can lead to a possibility that some data in
     * the surname column is not up to date (during the migration process lastName could have been updated).
     * In this case one can run yet another migration script after all applications have been deployed in the
     * new version to ensure that the surname field is updated.
     *
     * However it makes sense since when looking at the migration from 2.0.0 -> 3.0.0. In 3.0.0 we no longer
     * have a notion of lastName at all - so we don't update that column. If we rollback from 3.0.0 -> 2.0.0 if we
     * would be reading from lastName, then we would have very old data (since not a single datum was inserted
     * to lastName in version 3.0.0).
     */
    public String getSurname() {
        return this.surname != null ? this.surname : this.lastName;
    }

    /**
     * Storing both FIRST_NAME and SURNAME entries
     */
    public void setSurname(String surname) {
        this.lastName = surname;
        this.surname = surname;
    }

    @Override
    public String toString() {
        return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName + ", surname=" + this.surname
                + "]";
    }
}

3-қадам: кодтан фамилияны жою

Қолданба нұсқасы: 3.0.0

ДБ нұсқасы:v3

Пікір

Ескерту пер.: Шамасы, түпнұсқа мақалада автор осы блоктың мәтінін 2-қадамнан қате көшіріп алған. Бұл қадамда бағанды ​​пайдаланатын функционалдылықты жоюға бағытталған қолданба кодына өзгертулер енгізу керек. last_name.

Жаңа бағанды ​​қосу және оның мазмұнын көшіру арқылы біз кері үйлесімді дерекқор өзгерістерін жасадық. Сондай-ақ, егер JAR кері қайтарылса немесе ескі JAR жұмыс істеп тұрса, ол орындау кезінде үзілмейді.

Қолданбаны кері қайтару

Қазіргі уақытта бізде қолданба нұсқасы бар 3.0.0 және мәліметтер базасы v3. Нұсқа 3.0.0 деректерді сақтамайды last_name. Бұл дегеніміз, в surname ең өзекті ақпарат сақталады.

Қадамдар:

  1. қолданбаны нұсқаға қайтарыңыз 2.0.0.
  2. нұсқасы 2.0.0 пайдаланады және last_name и surname.
  3. нұсқасы 2.0.0 алады surname, егер ол нөл болмаса, әйтпесе -last_name

Мәліметтер базасының өзгеруі

Деректер базасында құрылымдық өзгерістер жоқ. Ескі деректердің соңғы тасымалдауын орындау үшін келесі сценарий орындалады:

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

Код өзгерістері

Ескерту пер.: Бұл блоктың сипаттамасын автор қателесіп 2-қадамнан көшірген. Мақаланың логикасына сәйкес, осы қадамдағы кодтағы өзгерістер одан бағанмен жұмыс істейтін элементтерді алып тастауға бағытталуы керек. last_name.

Біз деректерді сақтаймыз last_name, және surname. Сонымен қатар, біз бағаннан оқимыз 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 surname;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSurname() {
        return this.surname;
    }

    public void setSurname(String lastname) {
        this.surname = lastname;
    }

    @Override
    public String toString() {
        return "Person [firstName=" + this.firstName + ", surname=" + this.surname
                + "]";
    }
}

4-қадам: Дерекқордан фамилияны жою

Қолданба нұсқасы: 4.0.0

ДБ нұсқасы: v4

Пікір

Нұсқа кодына байланысты 3.0.0 бағанды ​​пайдаланбады last_name, егер біз қайтып оралсақ, орындау кезінде жаман ештеңе болмайды 3.0.0 дерекқордан бағанды ​​жойғаннан кейін.

Сценарийді орындау журналдары

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

ДҚ өзгереді

Салыстырмалы түрде v3 біз жай ғана бағанды ​​алып тастаймыз last_name және жетіспейтін шектеулерді қосыңыз.

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

Код өзгерістері

Кодта ешқандай өзгерістер жоқ.

қорытынды

Біз бірнеше кері үйлесімді орналастыруларды орындау арқылы кері үйлесімсіз баған атауын өзгертуді сәтті қолдандық. Төменде орындалған әрекеттердің қысқаша мазмұны берілген:

  1. қолданба нұсқасын қолдану 1.0.0 с v1 дерекқор схемасы (баған атауы = last_name)
  2. қолданба нұсқасын қолдану 2.0.0, деректерді сақтайтын last_name и surname. Өтініш келесіден оқиды last_name. Деректер базасы нұсқада v2сияқты бағандарды қамтиды last_name, Сонымен surname. surname л көшірмесі болып табыладыast_name. (ЕСКЕРТПЕ: Бұл бағанда бос емес шектеу болмауы керек)
  3. қолданба нұсқасын қолдану 3.0.0, ол тек деректерді сақтайды surname және фамилиясынан оқиды. Мәліметтер базасына келетін болсақ, соңғы көшу жүріп жатыр last_name в surname. Сондай-ақ шектеу ЖОҚ бастап жойылды last_name. Деректер базасы қазір нұсқада v3
  4. қолданба нұсқасын қолдану 4.0.0 - кодқа ешқандай өзгерістер енгізілмейді. Мәліметтер базасын орналастыру v4, ол жояды last_name. Мұнда дерекқорға кез келген жетіспейтін шектеулерді қосуға болады.

Осы тәсілді қолдана отырып, дерекқордың/қолданбаның үйлесімділігін бұзбай әрқашан бір нұсқаны кері қайтаруға болады.

код

Осы мақалада қолданылған барлық код мына жерден қол жетімді GitHub. Төменде қосымша сипаттама берілген.

жобалар

Репозиторийді клондағаннан кейін келесі қалта құрылымын көресіз.

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

Сценарийлер

Төмендегі сценарийлерде сипатталған сценарийлерді іске қосуға болады, олар дерекқорға кері үйлесімді және үйлеспейтін өзгерістерді көрсетеді.

Көру үшін кері үйлесімді өзгерістері бар жағдай, іске қосу:

./scripts/scenario_backward_compatible.sh

Және көру үшін кері үйлесімсіз өзгерістері бар жағдай, іске қосу:

./scripts/scenario_backward_incompatible.sh

Көктемгі аяқ киім үлгісі Flyway

Барлық мысалдар алынған Spring Boot Sample Flyway.

Қарап шығуға болады http://localhost:8080/flyway, сценарийлер тізімі бар.

Бұл мысал H2 консолін де қамтиды (ат http://localhost:8080/h2-console) осылайша дерекқор күйін көре аласыз (әдепкі jdbc URL мекенжайы jdbc:h2:mem:testdb).

қосымша

Сондай-ақ біздің блогтағы басқа мақалаларды оқыңыз:

Ақпарат көзі: www.habr.com

пікір қалдыру