Распоредување и бази на податоци со нула прекини

Распоредување и бази на податоци со нула прекини

Оваа статија детално објаснува како да се решат проблемите со компатибилноста на базата на податоци при распоредувањето. Ќе ви кажеме што може да се случи со вашите производствени апликации ако се обидете да ги распоредите без прелиминарна подготовка. Потоа ќе поминеме низ фазите на животниот циклус на апликацијата кои се потребни за да имаат нула прекини (прибл. лента: понатаму - нула застој). Резултатот од нашите операции ќе биде да ја примениме промената на базата на податоци некомпатибилна наназад на наназад компатибилен начин.

Ако сакате да ги разберете примерите на кодот од статијата, можете да ги најдете на GitHub.

Вовед

Нулта распоредување на застој

Колку мистично нула распоредување на застој? Може да се каже дека ова е кога вашата апликација е распоредена на таков начин што можете успешно да воведете нова верзија на апликацијата во производството, додека корисникот не ја забележува нејзината недостапност. Од перспектива на корисникот и компанијата, ова е најдоброто можно сценарио за распоредување бидејќи овозможува воведување нови функции и поправка на грешки без пречки.

Како да се постигне ова? Постојат неколку начини, еве еден од нив:

  • распоредете ја верзијата бр. 1 на вашата услуга
  • изврши миграција на базата на податоци
  • Распоредете ја верзијата број 2 на вашата услуга паралелно со верзијата број 1
  • штом ќе видите дека верзијата бр. 2 работи како што треба, отстранете ја верзијата бр. 1
  • направено!

Лесно, нели? За жал, не е толку едноставно, а подоцна ќе го разгледаме детално. Сега да провериме уште еден прилично вообичаен процес на распоредување - сино зелено распоредување.

Дали некогаш сте слушнале за сино зелено распоредување? Cloud Foundry го прави ова исклучително лесно. Само погледнете овој член,, каде што го опишуваме ова подетално. Накратко да резимираме, да ве потсетиме како да направите сино зелено распоредување:

  • осигурајте се дека работат две копии од вашиот код за производство („сина“ и „зелена“);
  • насочете го целиот сообраќај кон сината средина, т.е. така што производните URL-адреси укажуваат таму;
  • распоредување и тестирање на сите промени на апликациите во зелена средина;
  • менувајте urls од сина во зелена околина

Распоредувањето на сино зеленото е пристап кој ви овозможува лесно да воведете нови функции без да се грижите за прекин на производството. Ова се должи на фактот дека дури и ако нешто се случи, можете лесно да се вратите назад во претходната средина со едноставно „трипнување на прекинувачот“.

Откако ќе го прочитате сето горенаведено, може да го поставите прашањето: Каква врска има нула прекини со распоредувањето на сино зеленото?

Па, тие имаат доста заедничко, бидејќи одржувањето на две копии од иста средина бара двоен напор за нивно одржување. Ова е причината зошто некои тимови тврдат Мартин Фаулер, следете варијација на овој пристап:

Друга опција е да се користи истата база на податоци, создавајќи сино-зелени прекинувачи за слоевите на веб и домен. Во овој пристап, базата на податоци често може да биде проблем, особено кога треба да ја промените нејзината шема за поддршка на нова верзија на софтверот.

И тука доаѓаме до главниот проблем во оваа статија. База на податоци. Ајде уште еднаш да ја разгледаме оваа фраза.

изврши миграција на базата на податоци.

Сега треба да си го поставите прашањето - што ако промената на базата на податоци не е компатибилна наназад? Нема да ми пукне првата верзија на апликацијата? Всушност, токму тоа ќе се случи...

Значи, дури и покрај огромните придобивки од нула прекини / распоредување на сино зелено, компаниите имаат тенденција да го следат следниот побезбеден процес за распоредување на нивните апликации:

  • подготви пакет со нова верзија на апликацијата
  • исклучете апликација која работи
  • стартувајте скрипти за мигрирање на базата на податоци
  • распоредете и стартувајте нова верзија на апликацијата

Во оваа статија, ќе детализираме како можете да работите со вашата база на податоци и код за да ги искористите предностите од нула распоредување на прекини.

Проблеми со базата на податоци

Ако имате апликација без државјанство која не складира никакви податоци во базата на податоци, можете веднаш да добиете нула распоредување на прекини. За жал, повеќето софтвери треба да складираат податоци некаде. Ова е причината зошто треба да размислите двапати пред да направите какви било промени на колото. Пред да навлеземе во деталите за тоа како да ја промените шемата за да биде можно распоредување без прекини, прво да се фокусираме на шемата за верзии.

Шема за верзии

Во оваа статија ќе користиме Flyway како алатка за контрола на верзијата (прибл. Превод: зборуваме за миграции на бази на податоци). Секако, ќе напишеме и апликација Spring Boot која има вградена поддршка за Flyway и ќе врши миграција на шемата додека го поставува контекстот на апликацијата. Кога користите 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, добивате 2 големи придобивки:

  • ги одделувате промените на базата на податоци од промените на кодот
  • Миграцијата на базата на податоци се случува заедно со објавувањето на вашата апликација, т.е. вашиот процес на распоредување е поедноставен

Решавање проблеми со базата на податоци

Во следниот дел од статијата, ќе се фокусираме на разгледување на два пристапа за промени во базата на податоци.

  • наназад некомпатибилност
  • компатибилност наназад

Првото ќе се смета како предупредување дека не треба да вршите нула распоредување на застој без прелиминарна подготовка... Вториот нуди решение за тоа како можете да извршите распоредување без застој и во исто време да одржувате компатибилност наназад.

Нашиот проект на кој ќе работиме ќе биде едноставна апликација Spring Boot Flyway која има Person с first_name и last_name во базата на податоци (прибл. превод: Person е табела и ѓirst_name и last_name - тоа се полињата во него). Сакаме да се преименуваме last_name в surname.

Претпоставки

Пред да влеземе во деталите, има неколку претпоставки што треба да ги направиме за нашите апликации. Главниот резултат што сакаме да го постигнеме ќе биде прилично едноставен процес.

Заметка. Бизнис ПРО-СОВЕТ. Поедноставувањето на процесите може да ви заштеди многу пари за поддршка (колку повеќе луѓе имате што работат за вашата компанија, толку повеќе пари можете да заштедите)!

Нема потреба да се враќа базата на податоци

Ова го поедноставува процесот на распоредување (некои враќања на базата на податоци се речиси невозможни, како што е враќањето на бришењето). Претпочитаме да ги вратиме назад само апликациите. На овој начин, дури и ако имате различни бази на податоци (на пример, SQL и NoSQL), вашиот цевковод за распоредување ќе изгледа исто.

СЕКОГАШ мора да биде можно да се врати апликацијата една верзија назад (не повеќе)

Враќањето треба да се направи само кога е потребно. Ако има грешка во тековната верзија што не се поправа лесно, треба да можеме да се вратиме на најновата работна верзија. Претпоставуваме дека оваа најнова работна верзија е претходната. Одржувањето на компатибилноста на кодот и базата на податоци за повеќе од едно вклучување би било исклучително тешко и скапо.

Белешката. За поголема читливост, во оваа статија ќе ја промениме главната верзија на апликацијата.

Чекор 1: Почетна состојба

Верзија на апликација: 1.0.0
Верзија на DB: 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

Верзија на DB: v2bad

Коментар

Тековните промени НЕ ни дозволуваат да извршуваме две инстанци (стари и нови) истовремено. Така, ќе биде тешко да се постигне нула распоредување на застој (ако се земат предвид претпоставките, тоа е всушност невозможно).

А/Б тестирање

Сегашната ситуација е дека имаме верзија на апликација 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 (прибл. пер.: авторот веројатно мислел на 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

Извор скрипта 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
Верзија на DB: v2

Коментар

Со додавање нова колона и копирање на нејзината содржина, создаваме промени во базата на податоци компатибилни наназад. Во исто време, ако го вратиме JAR-от или имаме стар JAR кој работи, тој нема да се скрши за време на извршувањето.

Воведуваме нова верзија

Чекори:

  1. изврши миграција на базата на податоци за да се создаде нова колона surname. Сега вашата DB верзија v2
  2. копирајте податоци од last_name в surname. Обрни вниманиедека ако имате многу од овие податоци, треба да размислите за миграција на серии!
  3. напишете го кодот каде што се користат ДВЕТЕТО и новиИ стариот колона. Сега вашата верзија на апликацијата 2.0.0
  4. прочитајте ја вредноста од колоната surname, ако не е null, или од лast_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. Можете да го напуштите методот и да додадете проверки за null, но многу подобро решение би било да се увериме дека во логиката getSurname() ја избравте точната вредност што не е нула.

А/Б тестирање

Сегашната ситуација е дека имаме верзија на апликација 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, така што враќањето треба да биде успешно

DB се менува

Базата на податоци содржи колона со име 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: Отстранување на last_name од кодот

Верзија на апликација: 3.0.0

Верзија на DB: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: Отстранување на last_name од базата на податоци

Верзија на апликација: 4.0.0

Верзија на DB: 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"}

DB се менува

Релативно 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

Додадете коментар