Sıfır fasilə zamanı yerləşdirmə və verilənlər bazası

Sıfır fasilə zamanı yerləşdirmə və verilənlər bazası

Bu məqalə yerləşdirmə zamanı verilənlər bazası uyğunluğu problemlərinin necə həll olunacağını ətraflı izah edir. İlkin hazırlıq olmadan yerləşdirməyə cəhd etsəniz, istehsal proqramlarınızla nə baş verə biləcəyini sizə xəbər verəcəyik. Daha sonra sıfır dayanma müddəti olması tələb olunan tətbiqin həyat dövrü mərhələlərindən keçəcəyik (təqribən. zolaq: ​​daha çox - sıfır dayanma). Əməliyyatlarımızın nəticəsi geriyə uyğun olmayan verilənlər bazası dəyişikliyini geriyə uyğun şəkildə tətbiq etmək olacaq.

Məqalədəki kod nümunələrini anlamaq istəyirsinizsə, onları burada tapa bilərsiniz Github.

Giriş

Sıfır dayanma vaxtı yerləşdirmə

Nə mistikdir sıfır dayanma vaxtı yerləşdirmə? Deyə bilərsiniz ki, bu, tətbiqinizin elə yerləşdirildiyi zaman tətbiqin yeni versiyasını istehsala uğurla təqdim edə bilərsiniz, halbuki istifadəçi onun əlçatmazlığını hiss etmir. İstifadəçi və şirkət nöqteyi-nəzərindən bu, mümkün olan ən yaxşı yerləşdirmə ssenarisidir, çünki o, yeni funksiyaların təqdim edilməsinə və xətaların kəsilmədən düzəldilməsinə imkan verir.

Buna necə nail olmaq olar? Bir neçə yol var, onlardan biri budur:

  • xidmətinizin №1 versiyasını yerləşdirin
  • verilənlər bazası köçürməsini həyata keçirin
  • Xidmətinizin №2 versiyasını №1 versiyaya paralel olaraq yerləşdirin
  • 2-ci versiyanın lazım olduğu kimi işlədiyini görən kimi 1-ci versiyanı çıxarın
  • Hazır!

Asan, elə deyilmi? Təəssüf ki, bu o qədər də sadə deyil və biz bunu daha sonra ətraflı nəzərdən keçirəcəyik. İndi başqa kifayət qədər ümumi yerləşdirmə prosesini yoxlayaq - mavi yaşıl yerləşdirmə.

Heç eşitmisinizmi mavi yaşıl yerləşdirmə? Cloud Foundry bunu çox asanlaşdırır. Sadəcə baxın Bu məqalə, burada bunu daha ətraflı təsvir edirik. Qısaca ümumiləşdirmək üçün mavi yaşıl yerləşdirməni necə edəcəyinizi sizə xatırladaq:

  • istehsal kodunuzun iki nüsxəsinin (“mavi” və “yaşıl”) işləməsini təmin edin;
  • bütün trafiki mavi mühitə yönəldin, yəni. istehsal URL-lərinin oraya işarə etməsi üçün;
  • bütün tətbiq dəyişikliklərini yaşıl mühitdə yerləşdirmək və sınaqdan keçirmək;
  • URL-ləri mavi mühitdən yaşıl mühitə keçirin

Mavi yaşıl yerləşdirmə, istehsalın pozulmasından narahat olmadan asanlıqla yeni xüsusiyyətləri təqdim etməyə imkan verən bir yanaşmadır. Bu onunla bağlıdır ki, bir şey baş versə belə, sadəcə “keçici vurmaqla” asanlıqla əvvəlki mühitə qayıda bilərsiniz.

Yuxarıdakıların hamısını oxuduqdan sonra sual verə bilərsiniz: Sıfır fasilənin Mavi yaşıl yerləşdirmə ilə nə əlaqəsi var?

Bəli, onların çoxlu ortaq cəhətləri var, çünki eyni mühitin iki nüsxəsini saxlamaq onları saxlamaq üçün ikiqat səy tələb edir. Buna görə də bəzi komandalar iddia edir Martin Fowler, bu yanaşmanın bir variantını izləyin:

Başqa bir seçim, veb və domen təbəqələri üçün mavi-yaşıl keçidlər yaratmaqla eyni verilənlər bazasından istifadə etməkdir. Bu yanaşmada verilənlər bazası çox vaxt problem yarada bilər, xüsusən də proqram təminatının yeni versiyasını dəstəkləmək üçün onun sxemini dəyişmək lazım olduqda.

Və burada bu məqalədəki əsas problemə gəlirik. Verilənlər bazası. Gəlin bu ifadəyə bir daha nəzər salaq.

verilənlər bazası köçürməsini həyata keçirin.

İndi özünüzə sual verməlisiniz - əgər verilənlər bazası dəyişikliyi geriyə uyğun deyilsə? Proqramın ilk versiyası pozulmayacaq? Əslində belə olacaq...

Beləliklə, sıfır iş vaxtı / mavi yaşıl yerləşdirmənin böyük faydalarına baxmayaraq, şirkətlər tətbiqlərini yerləşdirmək üçün aşağıdakı daha təhlükəsiz prosesə əməl edirlər:

  • proqramın yeni versiyası ilə paket hazırlayın
  • işləyən tətbiqi bağlayın
  • verilənlər bazasını köçürmək üçün skriptləri işə salın
  • tətbiqin yeni versiyasını yerləşdirin və işə salın

Bu yazıda sıfır dayanma vaxtı yerləşdirmədən faydalanmaq üçün verilənlər bazanız və kodunuzla necə işləyə biləcəyinizi ətraflı izah edəcəyik.

Verilənlər bazası problemləri

Verilənlər bazasında heç bir məlumat saxlamayan vətəndaşlığı olmayan bir tətbiqiniz varsa, dərhal sıfır dayanma vaxtı yerləşdirmə əldə edə bilərsiniz. Təəssüf ki, əksər proqram təminatı məlumatları haradasa saxlamalıdır. Buna görə dövrədə hər hansı bir dəyişiklik etməzdən əvvəl iki dəfə düşünməlisiniz. Fasiləsiz yerləşdirmənin mümkün olması üçün sxemin necə dəyişdirilməsinin təfərrüatlarına keçməzdən əvvəl gəlin ilk növbədə versiya sxeminə diqqət yetirək.

Versiya sxemi

Bu yazıda istifadə edəcəyik Uçuş yolu versiyaya nəzarət aləti kimi (təqribən. Tərcümə: söhbət verilənlər bazası miqrasiyasından gedir). Təbii ki, biz həmçinin quraşdırılmış Flyway dəstəyi olan və proqram kontekstini qurarkən sxem miqrasiyasını həyata keçirəcək Spring Boot tətbiqi yazacağıq. Flyway istifadə edərkən, miqrasiya skriptlərini layihələr qovluğunda saxlaya bilərsiniz (standart olaraq classpath:db/migration). Burada belə miqrasiya fayllarının nümunəsini görə bilərsiniz

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

Bu nümunədə biz 4 köçürmə skriptini görürük ki, onlar əvvəllər icra olunmasalar, proqram başlayanda bir-birinin ardınca yerinə yetiriləcəklər. Fayllardan birinə baxaq (V1__init.sql) misal kimi.

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

Hər şey mükəmməl şəkildə özünü izah edir: verilənlər bazanızın necə dəyişdirilməsini müəyyən etmək üçün SQL-dən istifadə edə bilərsiniz. Spring Boot və Flyway haqqında daha çox məlumat üçün yoxlayın Spring Boot Sənədləri.

Spring Boot ilə mənbəyə nəzarət alətindən istifadə etməklə siz 2 böyük üstünlük əldə edirsiniz:

  • verilənlər bazası dəyişikliklərini kod dəyişikliklərindən ayırırsınız
  • Verilənlər bazasının miqrasiyası tətbiqinizin yayılması ilə birlikdə baş verir, yəni. yerləşdirmə prosesiniz sadələşdirilmişdir

Verilənlər bazası problemlərinin aradan qaldırılması

Məqalənin növbəti bölməsində verilənlər bazasında dəyişikliklərə iki yanaşmaya diqqət yetirəcəyik.

  • geriyə uyğunsuzluq
  • geriyə uyğunluq

Birincisi, ilkin hazırlıq olmadan sıfır fasiləsiz yerləşdirmə yerinə yetirməməyiniz barədə xəbərdarlıq kimi qəbul ediləcək... İkincisi, yerləşdirməni fasiləsiz olaraq necə həyata keçirə biləcəyiniz və eyni zamanda geriyə uyğunluğu qoruyub saxlaya biləcəyiniz barədə bir həll təklif edir.

Üzərində işləyəcəyimiz layihəmiz sadə Spring Boot Flyway proqramı olacaq Person с first_name и last_name verilənlər bazasında (təqribən. tərcümə: Person masadır və first_name и last_name - bunlar onun içindəki sahələrdir). Adını dəyişmək istəyirik last_name в surname.

Fərziyyələr

Təfərrüatlara keçməzdən əvvəl tətbiqlərimizlə bağlı etməli olduğumuz bir neçə fərziyyə var. Əldə etmək istədiyimiz əsas nəticə kifayət qədər sadə proses olacaq.

Qeyd. Biznes PRO-TIP. Proseslərin sadələşdirilməsi sizə dəstək üçün çoxlu pula qənaət edə bilər (şirkətinizdə nə qədər çox insan işləyirsə, bir o qədər çox pula qənaət edə bilərsiniz)!

Verilənlər bazasını geri qaytarmağa ehtiyac yoxdur

Bu, yerləşdirmə prosesini asanlaşdırır (bəzi verilənlər bazasının geri qaytarılması demək olar ki, mümkün deyil, məsələn, silinmənin geri qaytarılması). Biz yalnız proqramları geri qaytarmağa üstünlük veririk. Bu yolla, müxtəlif verilənlər bazanız olsa belə (məsələn, SQL və NoSQL), yerləşdirmə boru kəməriniz eyni görünəcək.

Proqramın bir versiyasını geri qaytarmaq HƏMİŞƏ mümkün olmalıdır (daha çox deyil)

Geri qaytarma yalnız lazım olduqda edilməlidir. Cari versiyada asanlıqla düzəldilməyən bir səhv varsa, biz ən son işləyən versiyaya qayıda bilməliyik. Güman edirik ki, bu son işləyən versiya əvvəlki versiyadır. Birdən çox buraxılış üçün kod və verilənlər bazası uyğunluğunu saxlamaq olduqca çətin və bahalı olardı.

Qeyd. Daha çox oxunaqlı olmaq üçün bu məqalədə tətbiqin əsas versiyasını dəyişəcəyik.

Addım 1: İlkin vəziyyət

Tətbiq versiyası: 1.0.0
DB versiyası: v1

Qeyd

Bu, tətbiqin ilkin vəziyyəti olacaq.

Verilənlər bazası dəyişiklikləri

DB ehtiva edir 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');

Kod dəyişiklikləri

Tətbiq şəxs məlumatlarını saxlayır 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
                + "]";
    }
}

Geriyə uyğun olmayan sütun adının dəyişdirilməsi

Sütun adının dəyişdirilməsi nümunəsinə baxaq:

Diqqət. Aşağıdakı nümunə şeyləri qəsdən pozacaq. Biz bunu verilənlər bazası uyğunluğu problemini nümayiş etdirmək üçün göstəririk.

Tətbiq versiyası: 2.0.0.BAD

DB versiyası: v2bad

Qeyd

Mövcud dəyişikliklər eyni anda iki nümunəni (köhnə və yeni) işlətməyə imkan vermir. Beləliklə, sıfır fasiləsiz yerləşdirməyə nail olmaq çətin olacaq (əgər fərziyyələr nəzərə alınarsa, bu, əslində mümkün deyil).

A/B testi

Mövcud vəziyyət ondan ibarətdir ki, bizim proqram versiyamız var 1.0.0, istehsalda və verilənlər bazasında yerləşdirilir v1. Tətbiqin ikinci nüsxəsini, versiyasını yerləşdirməliyik 2.0.0.BAD, və verilənlər bazasını yeniləyin v2bad.

Addımlar:

  1. versiya tətbiqinin yeni nümunəsi yerləşdirilir 2.0.0.BADverilənlər bazasını yeniləyir v2bad
  2. verilənlər bazasında v2bad sütun last_name artıq mövcud deyil - olaraq dəyişdirildi surname
  3. Verilənlər bazası və proqram yeniləməsi uğurlu oldu və bəzi nümunələr işləyir 1.0.0, digərləri - in 2.0.0.BAD. Hər şey verilənlər bazasına bağlıdır v2bad
  4. versiyanın bütün nümunələri 1.0.0 sütuna məlumat daxil etməyə çalışacaqları üçün səhvlər atmağa başlayacaqlar last_nameartıq mövcud olmayan
  5. versiyanın bütün nümunələri 2.0.0.BAD problemsiz işləyəcək

Gördüyünüz kimi, verilənlər bazası və tətbiqdə geriyə uyğun olmayan dəyişikliklər etsək, A/B testi mümkün deyil.

Tətbiqin geri qaytarılması

Fərz edək ki, A/B yerləşdirməyə cəhd etdikdən sonra (təqribən. per.: müəllif burada yəqin ki, A/B testini nəzərdə tuturdu) biz qərara gəldik ki, tətbiqi versiyaya geri qaytarmalıyıq 1.0.0. Tutaq ki, verilənlər bazasını geri qaytarmaq istəmirik.

Addımlar:

  1. versiya tətbiqi nümunəsini dayandırırıq 2.0.0.BAD
  2. verilənlər bazası hələ də qalır v2bad
  3. versiyadan bəri 1.0.0 nə olduğunu başa düşmür surname, səhvləri görəcəyik
  4. cəhənnəm dağıldı, daha geri dönə bilmərik

Gördüyünüz kimi, verilənlər bazası və proqramda geriyə uyğun olmayan dəyişikliklər etsək, əvvəlki versiyaya qayıda bilmərik.

Skript icra qeydləri

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

Verilənlər bazası dəyişiklikləri

Adını dəyişdirən miqrasiya skripti last_name в surname

Mənbə Flyway skripti:

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

Adını dəyişdirən skript last_name.

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

Kod dəyişiklikləri

Sahənin adını dəyişdik lastName haqqında surname.

Sütunun adının geriyə uyğun şəkildə dəyişdirilməsi

Bu, qarşılaşa biləcəyimiz ən ümumi vəziyyətdir. Geriyə uyğun olmayan dəyişikliklər etməliyik. Biz artıq sübut etmişik ki, sıfır fasiləsiz yerləşdirmə üçün əlavə addımlar olmadan sadəcə verilənlər bazası miqrasiyasını tətbiq etməməliyik. Məqalənin bu hissəsində geriyə uyğunluğu qoruyaraq istənilən nəticəni əldə etmək üçün verilənlər bazası köçürmələri ilə birlikdə tətbiqin 3 yerləşdirilməsini həyata keçirəcəyik.

Qeyd. Xatırladaq ki, versiya bazamız var v1. Sütunları ehtiva edir first_name и last_name. Biz dəyişməliyik last_name haqqında surname. Proqram versiyamız da var 1.0.0, hələ istifadə olunmayan surname.

Addım 2: Soyad əlavə edin

Tətbiq versiyası: 2.0.0
DB versiyası: v2

Qeyd

Yeni sütun əlavə etmək və onun məzmununu kopyalamaqla biz geriyə uyğun verilənlər bazası dəyişiklikləri yaradırıq. Eyni zamanda, əgər biz JAR-ı geri qaytarsaq və ya köhnə JAR işləyirsə, icra zamanı o, qırılmayacaq.

Yeni versiyanı təqdim edirik

Addımlar:

  1. yeni sütun yaratmaq üçün verilənlər bazası köçürməsini həyata keçirin surname. İndi DB versiyanız v2
  2. məlumatları kopyalayın last_name в surname. Xahiş edirikki, bu məlumatların çoxu varsa, toplu miqrasiyanı nəzərdən keçirməlisiniz!
  3. onların istifadə olunduğu kodu yazın İKİSİ и yeniköhnə sütun. İndi proqram versiyanız 2.0.0
  4. sütundan dəyəri oxuyun surname, əgər belə deyilsə null, və ya l-dənast_name, əgər surname qeyd edilməmişdir. Siz silə bilərsiniz getLastName() koddan, çünki o, çıxış edəcək null tətbiqinizi geri qaytararkən 3.0.0 üzrə 2.0.0.

Spring Boot Flyway istifadə edirsinizsə, bu iki addım versiyanın işə salınması zamanı yerinə yetiriləcək 2.0.0 tətbiqlər. Verilənlər bazası versiya alətini əl ilə işlədirsinizsə, bunu etmək üçün iki fərqli şey etməli olacaqsınız (əvvəlcə db versiyasını əl ilə yeniləyin və sonra yeni tətbiqi yerləşdirin).

Bu vacibdir. Unutmayın ki, yeni yaradılmış sütun OLMAMALI olmaq NULL DEYİL. Geri qaytarma etsəniz, köhnə proqram yeni sütun haqqında bilmir və bu müddət ərzində onu quraşdırmayacaq Insert. Ancaq bu məhdudiyyəti əlavə etsəniz, db olacaq v2, bu, yeni sütunun dəyərini təyin etməyi tələb edəcək. Hansı ki, məhdudiyyətlərin pozulmasına gətirib çıxaracaq.

Bu vacibdir. Siz metodu aradan qaldırmalısınız getLastName(), çünki versiyada 3.0.0 Kodda sütun anlayışı yoxdur last_name. Bu o deməkdir ki, orada null təyin olunacaq. Siz metodu tərk edib çeklər əlavə edə bilərsiniz null, amma məntiqdə əmin olmaq daha yaxşı bir həll olardı getSurname() siz düzgün sıfırdan fərqli dəyəri seçdiniz.

A/B testi

Mövcud vəziyyət ondan ibarətdir ki, bizim proqram versiyamız var 1.0.0, istehsalda yerləşdirilmiş və verilənlər bazası v1. Versiya tətbiqinin ikinci nümunəsini yerləşdirməliyik 2.0.0verilənlər bazasını yeniləyəcək v2.

Addımlar:

  1. versiya tətbiqinin yeni nümunəsi yerləşdirilir 2.0.0verilənlər bazasını yeniləyir v2
  2. bu vaxt bəzi sorğular versiya nümunələri tərəfindən işlənmişdir 1.0.0
  3. yeniləmə uğurlu oldu və sizdə versiya tətbiqinin çoxlu işləyən nümunələri var 1.0.0 və digər versiyalar 2.0.0. Hər kəs verilənlər bazası ilə əlaqə saxlayır v2
  4. versiyası 1.0.0 verilənlər bazasında soyad sütunundan deyil, versiyadan istifadə edir 2.0.0 istifadə edir. Onlar bir-birinə qarışmırlar və heç bir səhv olmamalıdır.
  5. versiyası 2.0.0 məlumatları həm köhnə, həm də yeni sütunda saxlayır, geriyə uyğunluğu təmin edir

Bu vacibdir. Köhnə/yeni sütundakı dəyərlərə əsaslanan elementləri sayan hər hansı sorğunuz varsa, indi dublikat dəyərlərinizin olduğunu xatırlamalısınız (çox güman ki, onlar hələ də köçürlər). Məsələn, soyadı (sütun nə adlanırsa) hərflə başlayan istifadəçilərin sayını saymaq istəyirsinizsə A, sonra məlumat miqrasiyası tamamlanana qədər (oldnew sütun) yeni sütuna sorğu göndərsəniz, uyğun olmayan məlumatınız ola bilər.

Tətbiqin geri qaytarılması

İndi proqram versiyamız var 2.0.0 və verilənlər bazası v2.

Addımlar:

  1. tətbiqinizi versiyaya geri qaytarın 1.0.0.
  2. versiyası 1.0.0 verilənlər bazasında sütundan istifadə etmir surname, belə ki, geri qaytarma uğurlu olmalıdır

DB dəyişiklikləri

Verilənlər bazasında adlı sütun var last_name.

Flyway mənbə skripti:

CREATE TABLE PERSON (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
first_name varchar(255) not null,
last_name varchar(255) not null
);

insert into PERSON (first_name, last_name) values ('Dave', 'Syer');

Skript əlavə edin surname.

Diqqət. Unutmayın ki, siz əlavə etdiyiniz sütuna heç bir NOT NULL məhdudiyyəti ƏLAVƏ EDƏ BİLMƏZSİNİZ. JAR-ı geri qaytarsanız, köhnə versiyanın əlavə edilmiş sütun haqqında heç bir fikri olmayacaq və onu avtomatik olaraq NULL-ə təyin edəcək. Belə bir məhdudiyyət varsa, köhnə proqram sadəcə pozulacaq.

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

Kod dəyişiklikləri

Biz məlumatları olaraq saxlayırıq last_name, və surname. Eyni zamanda biz oxuyuruq last_name, çünki bu sütun ən uyğundur. Yerləşdirmə prosesi zamanı bəzi sorğular hələ yenilənməmiş tətbiq nümunəsi tərəfindən işlənmiş ola bilər.

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

Addım 3: Koddan soyadın silinməsi

Tətbiq versiyası: 3.0.0

DB versiyası:v3

Qeyd

Qeyd per.: Göründüyü kimi, orijinal məqalədə müəllif səhvən bu blokun mətnini 2-ci addımdan köçürüb. Bu addımda, sütundan istifadə edən funksionallığın aradan qaldırılmasına yönəlmiş proqram kodunda dəyişikliklər edilməlidir. last_name.

Yeni sütun əlavə etmək və onun məzmununu kopyalamaqla biz geriyə uyğun verilənlər bazası dəyişiklikləri yaratdıq. Həmçinin, əgər biz JAR-ı geri qaytarsaq və ya köhnə JAR işləyirsə, o, icra zamanı pozulmayacaq.

Tətbiqin geri qaytarılması

Hazırda proqram versiyamız var 3.0.0 və verilənlər bazası v3. Versiya 3.0.0 məlumatı saxlamır last_name. Bu o deməkdir ki, in surname ən aktual məlumatlar saxlanılır.

Addımlar:

  1. tətbiqinizi versiyaya geri qaytarın 2.0.0.
  2. versiyası 2.0.0 istifadə edir və last_name и surname.
  3. versiyası 2.0.0 alacaq surname, sıfır deyilsə, əks halda -last_name

Verilənlər bazası dəyişiklikləri

Verilənlər bazasında struktur dəyişikliyi yoxdur. Köhnə məlumatların son miqrasiyasını həyata keçirmək üçün aşağıdakı skript icra olunur:

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

Kod dəyişiklikləri

Qeyd per.: Bu blokun təsviri də müəllif tərəfindən səhvən 2-ci addımdan köçürülüb. Məqalənin məntiqinə uyğun olaraq, bu addımda kodda edilən dəyişikliklər sütunla işləyən elementlərin ondan çıxarılmasına yönəldilməlidir. last_name.

Biz məlumatları olaraq saxlayırıq last_name, və surname. Bundan əlavə, sütundan oxuyuruq last_name, çünki ən aktualdır. Yerləşdirmə prosesi zamanı bəzi sorğular hələ təkmilləşdirilməmiş instansiya tərəfindən işlənə bilər.

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

Addım 4: soyadın verilənlər bazasından silinməsi

Tətbiq versiyası: 4.0.0

DB versiyası: v4

Qeyd

Versiya koduna görə 3.0.0 sütundan istifadə etmədi last_name, geri dönsək, icra zamanı pis bir şey olmayacaq 3.0.0 verilənlər bazasından sütunu sildikdən sonra.

Skript icra qeydləri

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 dəyişiklikləri

Nisbətən v3 biz sadəcə sütunu çıxarırıq last_name və itkin məhdudiyyətlər əlavə edin.

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

Kod dəyişiklikləri

Kodda heç bir dəyişiklik yoxdur.

Buraxılış

Bir neçə geriyə uyğun yerləşdirmə həyata keçirərək geriyə uyğun olmayan sütun adı dəyişikliyini uğurla tətbiq etdik. Aşağıda yerinə yetirilən hərəkətlərin xülasəsi verilmişdir:

  1. proqram versiyasının yerləşdirilməsi 1.0.0 с v1 verilənlər bazası sxemi (sütun adı = last_name)
  2. proqram versiyasının yerləşdirilməsi 2.0.0, məlumatları saxlayan last_name и surname. Tətbiqdən oxunur last_name. Verilənlər bazası versiyadadır v2kimi sütunları ehtiva edir last_namesurname. surname l-in surətidirast_name. (QEYD: Bu sütunun null olmayan məhdudiyyəti olmamalıdır)
  3. proqram versiyasının yerləşdirilməsi 3.0.0, yalnız məlumatları saxlayır surname və soyaddan oxuyur. Verilənlər bazasına gəlincə, sonuncu miqrasiya baş verir last_name в surname. Həm də məhdudiyyət NULL DEYİL -dən geri çəkildi last_name. Verilənlər bazası artıq versiyadadır v3
  4. proqram versiyasının yerləşdirilməsi 4.0.0 - kodda heç bir dəyişiklik edilmir. Verilənlər bazasının yerləşdirilməsi v4, aradan qaldıran last_name. Burada verilənlər bazasına çatışmayan məhdudiyyətləri əlavə edə bilərsiniz.

Bu yanaşmaya riayət etməklə verilənlər bazası/tətbiq uyğunluğunu pozmadan hər zaman bir versiyanı geri qaytara bilərsiniz.

Kod

Bu məqalədə istifadə olunan bütün kodlar burada mövcuddur Github. Aşağıda əlavə təsvir var.

Layihələr

Repozitoriyanın klonlanmasından sonra aşağıdakı qovluq strukturunu görəcəksiniz.

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

Aşağıdakı skriptlərdə təsvir olunan skriptləri işlədə bilərsiniz, bu, verilənlər bazasında geriyə uyğun və uyğun olmayan dəyişiklikləri nümayiş etdirəcək.

Görmək üçün geriyə uyğun dəyişikliklərin olduğu halda, qaçış:

./scripts/scenario_backward_compatible.sh

Və görmək üçün geriyə uyğun olmayan dəyişikliklərin olduğu halda, qaçış:

./scripts/scenario_backward_incompatible.sh

Bahar çəkmə nümunəsi uçuş yolu

Bütün nümunələr götürülmüşdür Spring Boot Sample Flyway.

Baxa bilersiniz http://localhost:8080/flyway, skriptlərin siyahısı var.

Bu nümunəyə H2 konsolu da daxildir (at http://localhost:8080/h2-console) beləliklə verilənlər bazası statusuna baxa bilərsiniz (defolt jdbc URL-dir jdbc:h2:mem:testdb).

Bundan əlavə,

Bloqumuzdakı digər məqalələri də oxuyun:

Mənbə: www.habr.com

Добавить комментарий