Nol ishlamay qolishi va ma'lumotlar bazalari

Nol ishlamay qolishi va ma'lumotlar bazalari

Ushbu maqola tarqatishda ma'lumotlar bazasi mosligi bilan bog'liq muammolarni qanday hal qilishni batafsil tushuntiradi. Agar siz dastlabki tayyorgarliksiz joylashtirishga harakat qilsangiz, ishlab chiqarish ilovalaringizga nima bo'lishi mumkinligini aytib beramiz. Keyin biz nol ishlamay qolish uchun talab qilinadigan dasturning hayot aylanish bosqichlaridan o'tamiz (taxminan. qator: keyingi - nol ishlamay qolish vaqti). Bizning operatsiyalarimiz natijasi orqaga mos kelmaydigan ma'lumotlar bazasi o'zgarishini orqaga mos keladigan tarzda qo'llash bo'ladi.

Agar siz maqoladagi kod misollarini tushunmoqchi bo'lsangiz, ularni quyidagi manzilda topishingiz mumkin GitHub.

kirish

Nol ishlamay qolishi

Qanday mistik nol ishlamay qolishi? Aytish mumkinki, bu sizning ilovangiz shu tarzda joylashtirilganki, siz dasturning yangi versiyasini ishlab chiqarishga muvaffaqiyatli joriy qilishingiz mumkin, ammo foydalanuvchi uning mavjud emasligini sezmaydi. Foydalanuvchi va kompaniya nuqtai nazaridan, bu mumkin bo'lgan eng yaxshi joylashtirish stsenariysi, chunki u yangi xususiyatlarni joriy qilish va xatolarni uzilishlarsiz tuzatish imkonini beradi.

Bunga qanday erishish mumkin? Bir necha usullar mavjud, ulardan biri:

  • xizmatingizning №1 versiyasini o'rnating
  • ma'lumotlar bazasini ko'chirishni amalga oshirish
  • Xizmatingizning №2 versiyasini №1 versiyaga parallel ravishda joylashtiring
  • 2-versiya kerakli darajada ishlayotganini ko'rganingizdan so'ng, 1-versiyani olib tashlang
  • tayyor!

Oson, shunday emasmi? Afsuski, bu unchalik oddiy emas va biz buni keyinroq batafsil ko'rib chiqamiz. Keling, yana bir keng tarqalgan joylashtirish jarayonini ko'rib chiqaylik - ko'k yashil joylashtirish.

Hech eshitganmisiz ko'k yashil joylashtirish? Cloud Foundry buni juda oson qiladi. Faqat qarang Ushbu maqola, bu erda biz buni batafsilroq tasvirlaymiz. Qisqacha xulosa qilish uchun sizga ko'k yashil joylashtirishni qanday qilishni eslatib o'tamiz:

  • ishlab chiqarish kodingizning ikki nusxasi (“ko‘k” va “yashil”) ishlashiga ishonch hosil qiling;
  • barcha trafikni ko'k muhitga yo'naltirish, ya'ni. ishlab chiqarish URL manzillari u yerga ishora qilishlari uchun;
  • yashil muhitda barcha dastur o'zgarishlarini joylashtirish va sinab ko'rish;
  • urllarni ko'kdan yashil muhitga o'tkazing

Moviy yashil joylashtirish - bu ishlab chiqarishni buzish haqida tashvishlanmasdan yangi xususiyatlarni osongina kiritish imkonini beruvchi yondashuv. Buning sababi, agar biror narsa yuz bergan bo'lsa ham, oddiygina "kalitni bosish" orqali oldingi muhitga osongina qaytishingiz mumkin.

Yuqoridagilarning barchasini o'qib chiqqandan so'ng, siz savol berishingiz mumkin: nol ishlamay qolishning Moviy yashil tarqatish bilan qanday aloqasi bor?

Ularning umumiy jihatlari juda ko'p, chunki bir xil muhitning ikkita nusxasini saqlash ularni saqlash uchun ikki baravar kuch talab qiladi. Shuning uchun ba'zi jamoalar da'vo qilmoqda Martin Fauler, ushbu yondashuvning o'zgarishiga amal qiling:

Yana bir variant - veb va domen qatlamlari uchun ko'k-yashil kalitlarni yaratish, bir xil ma'lumotlar bazasidan foydalanish. Ushbu yondashuvda ma'lumotlar bazasi ko'pincha muammo bo'lishi mumkin, ayniqsa dasturiy ta'minotning yangi versiyasini qo'llab-quvvatlash uchun uning sxemasini o'zgartirish kerak bo'lganda.

Va bu erda biz ushbu maqoladagi asosiy muammoga keldik. Malumotlar bazasi. Keling, ushbu iborani yana bir bor ko'rib chiqaylik.

ma'lumotlar bazasini ko'chirishni amalga oshirish.

Endi siz o'zingizga savol berishingiz kerak - agar ma'lumotlar bazasi o'zgarishi orqaga qarab mos kelmasa nima bo'ladi? Ilovaning birinchi versiyasi buzilmaydimi? Aslida, aynan shunday bo'ladi ...

Shunday qilib, nol ishlamay qolish / ko'k yashil o'rnatishning katta afzalliklariga qaramay, kompaniyalar o'z ilovalarini joylashtirish uchun quyidagi xavfsizroq jarayonga amal qilishadi:

  • ilovaning yangi versiyasi bilan paketni tayyorlang
  • ishlayotgan dasturni o'chirib qo'ying
  • ma'lumotlar bazasini ko'chirish uchun skriptlarni ishga tushiring
  • ilovaning yangi versiyasini o'rnatish va ishga tushirish

Ushbu maqolada biz o'z ma'lumotlar bazasi va kodingiz bilan qanday ishlashingiz mumkinligini batafsil ko'rib chiqamiz.

Ma'lumotlar bazasi bilan bog'liq muammolar

Agar sizda ma'lumotlar bazasida hech qanday ma'lumot saqlamaydigan fuqaroligi bo'lmagan ilovangiz bo'lsa, siz darhol nol ishlamay qolishi mumkin. Afsuski, ko'pchilik dasturiy ta'minot ma'lumotlarni biror joyda saqlashi kerak. Shuning uchun sxemaga biron bir o'zgartirish kiritishdan oldin ikki marta o'ylab ko'rishingiz kerak. Sxemani uzilishsiz ishga tushirish mumkin bo'lishi uchun qanday o'zgartirish haqida batafsil ma'lumotga kirishdan oldin, avval versiya sxemasiga e'tibor qarataylik.

Versiyalash sxemasi

Ushbu maqolada biz foydalanamiz Flyway versiyani boshqarish vositasi sifatida (taxminan. Tarjima: biz ma'lumotlar bazasi migratsiyasi haqida gapiramiz). Tabiiyki, biz o'rnatilgan Flyway-ni qo'llab-quvvatlaydigan Spring Boot ilovasini ham yozamiz va dastur kontekstini o'rnatishda sxema migratsiyasini amalga oshiramiz. Flyway-dan foydalanganda siz ko'chish skriptlarini loyihalaringiz papkasida saqlashingiz mumkin (standart bo'yicha classpath:db/migration). Bu erda siz bunday migratsiya fayllari misolini ko'rishingiz mumkin

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

Ushbu misolda biz 4 ta migratsiya skriptini ko'ramiz, agar ilgari bajarilmagan bo'lsa, dastur boshlanganda birin-ketin bajariladi. Keling, fayllardan birini ko'rib chiqaylik (V1__init.sql) misol sifatida.

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

Hammasi o'z-o'zidan tushunarli: ma'lumotlar bazasini qanday o'zgartirish kerakligini aniqlash uchun SQL dan foydalanishingiz mumkin. Spring Boot va Flyway haqida ko'proq ma'lumot olish uchun qarang Bahor yuklash hujjatlari.

Spring Boot bilan manbalarni boshqarish vositasidan foydalanib, siz ikkita katta afzalliklarga ega bo'lasiz:

  • siz ma'lumotlar bazasi o'zgarishlarini kod o'zgarishlaridan ajratasiz
  • Ma'lumotlar bazasini ko'chirish ilovangizni ishga tushirish bilan birga sodir bo'ladi, ya'ni. joylashtirish jarayoni soddalashtirilgan

Ma'lumotlar bazasi bilan bog'liq muammolarni bartaraf etish

Maqolaning keyingi qismida biz ma'lumotlar bazasini o'zgartirishning ikkita yondashuvini ko'rib chiqamiz.

  • orqaga qarab mos kelmaslik
  • orqaga qarab muvofiqlik

Birinchisi, dastlabki tayyorgarliksiz nol ishlamay qo'yishni amalga oshirmaslik kerakligi haqida ogohlantirish sifatida ko'rib chiqiladi ... Ikkinchisi, qanday qilib to'xtab qolmasdan joylashtirishni amalga oshirish va shu bilan birga orqaga qarab muvofiqlikni saqlab qolish bo'yicha yechim taklif qiladi.

Biz ustida ishlayotgan loyihamiz oddiy Spring Boot Flyway ilovasi bo'ladi Person с first_name и last_name ma'lumotlar bazasida (taxminan. tarjima: Person jadval va first_name и last_name - bu undagi maydonlar). Biz nomini o'zgartirmoqchimiz last_name в surname.

Taxminlar

Tafsilotlarga kirishdan oldin, ilovalarimiz haqida bir nechta taxminlarni aytishimiz kerak. Biz erishmoqchi bo'lgan asosiy natija juda oddiy jarayon bo'ladi.

Eslatma. Biznes PRO-TIP. Jarayonlarni soddalashtirish sizni qo'llab-quvvatlash uchun juda ko'p pulni tejashga yordam beradi (kompaniyangizda qancha odam ishlasa, shuncha ko'p pulni tejashingiz mumkin)!

Ma'lumotlar bazasini orqaga qaytarish kerak emas

Bu joylashtirish jarayonini soddalashtiradi (ba'zi ma'lumotlar bazasini qaytarish deyarli mumkin emas, masalan, o'chirishni qaytarish). Biz faqat ilovalarni orqaga qaytarishni afzal ko'ramiz. Shunday qilib, sizda turli xil ma'lumotlar bazalariga ega bo'lsangiz ham (masalan, SQL va NoSQL), joylashtirish quvur liniyasi bir xil ko'rinadi.

Ilovaning bir versiyasini orqaga qaytarish har doim ham mumkin bo'lishi kerak (ortiqqa emas)

Orqaga qaytarish faqat kerak bo'lganda amalga oshirilishi kerak. Agar joriy versiyada osonlikcha tuzatib bo'lmaydigan xatolik mavjud bo'lsa, biz oxirgi ishlaydigan versiyaga qaytishimiz kerak. Biz ushbu so'nggi ishlaydigan versiya avvalgisi deb taxmin qilamiz. Bir nechta tarqatish uchun kod va ma'lumotlar bazasi mosligini ta'minlash juda qiyin va qimmat bo'ladi.

Eslatma. Ko'proq o'qilishi uchun ushbu maqolada biz ilovaning asosiy versiyasini o'zgartiramiz.

1-qadam: Dastlabki holat

Ilova versiyasi: 1.0.0
JB versiyasi: v1

izoh

Bu dasturning dastlabki holati bo'ladi.

Ma'lumotlar bazasi o'zgarishi

JB o'z ichiga oladi 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 o'zgaradi

Ilova Shaxs ma'lumotlarini saqlaydi 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
                + "]";
    }
}

Orqaga mos kelmaydigan ustun nomini o'zgartirish

Keling, ustun nomini o'zgartirish misolini ko'rib chiqaylik:

Diqqat. Quyidagi misol narsalarni ataylab buzadi. Biz buni ma'lumotlar bazasi mosligi muammosini ko'rsatish uchun ko'rsatamiz.

Ilova versiyasi: 2.0.0.BAD

JB versiyasi: v2bad

izoh

Joriy o'zgarishlar bizga bir vaqtning o'zida ikkita nusxani (eski va yangi) ishga tushirishga ruxsat bermaydi. Shunday qilib, nol uzilish vaqtini joylashtirishga erishish qiyin bo'ladi (agar taxminlar hisobga olinsa, bu aslida mumkin emas).

A/B testi

Hozirgi vaziyat shundan iboratki, bizda dastur versiyasi mavjud 1.0.0, ishlab chiqarishda va ma'lumotlar bazasida joylashtirilgan v1. Biz ilovaning ikkinchi nusxasini, versiyasini joylashtirishimiz kerak 2.0.0.BAD, va ma'lumotlar bazasini yangilang v2bad.

Qadamlar:

  1. versiya ilovasining yangi nusxasi o'rnatildi 2.0.0.BADma'lumotlar bazasini yangilaydi v2bad
  2. ma'lumotlar bazasida v2bad ustun last_name endi mavjud emas - deb o'zgartirildi surname
  3. Ma'lumotlar bazasi va ilovalarni yangilash muvaffaqiyatli bo'ldi va ba'zi misollar ishlamoqda 1.0.0, boshqalar - ichida 2.0.0.BAD. Hamma narsa ma'lumotlar bazasiga ulangan v2bad
  4. versiyaning barcha namunalari 1.0.0 xatolarni tashlashni boshlaydi, chunki ular ustunga ma'lumotlarni kiritishga harakat qilishadi last_namekim endi mavjud emas
  5. versiyaning barcha namunalari 2.0.0.BAD muammosiz ishlaydi

Ko'rib turganingizdek, agar biz ma'lumotlar bazasi va ilovaga orqaga mos kelmaydigan o'zgarishlar kiritsak, A/B testini o'tkazish mumkin emas.

Ilovani orqaga qaytarish

Faraz qilaylik, A/B o'rnatishga urinib ko'rganingizdan so'ng (taxminan. per.: muallif bu erda A/B testini nazarda tutgan bo'lishi mumkin) biz ilovani versiyaga qaytarishimiz kerak deb qaror qildik 1.0.0. Aytaylik, biz ma'lumotlar bazasini orqaga qaytarishni xohlamaymiz.

Qadamlar:

  1. biz versiya ilova nusxasini to'xtatamiz 2.0.0.BAD
  2. ma'lumotlar bazasi hali ham mavjud v2bad
  3. versiyadan beri 1.0.0 nima ekanligini tushunmaydi surname, biz xatolarni ko'ramiz
  4. do'zax buzildi, biz endi orqaga qaytolmaymiz

Ko'rib turganingizdek, agar biz ma'lumotlar bazasi va ilovaga orqaga mos kelmaydigan o'zgarishlar kiritsak, avvalgi versiyaga qaytolmaymiz.

Skriptni bajarish jurnallari

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

Ma'lumotlar bazasi o'zgarishi

Nomini o'zgartiruvchi migratsiya skripti last_name в surname

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

Nomini o'zgartiradigan skript last_name.

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

Kod o'zgaradi

Biz maydon nomini o'zgartirdik lastName haqida surname.

Ustun nomini orqaga mos keladigan tarzda o'zgartirish

Bu biz duch kelishi mumkin bo'lgan eng keng tarqalgan holat. Biz orqaga mos kelmaydigan o'zgarishlarni amalga oshirishimiz kerak. Biz allaqachon isbotladikki, nol ishlamay qo'yish uchun biz qo'shimcha qadamlarsiz ma'lumotlar bazasi migratsiyasini qo'llamasligimiz kerak. Maqolaning ushbu bo'limida biz orqaga qarab muvofiqlikni saqlab, kerakli natijaga erishish uchun ma'lumotlar bazasini ko'chirish bilan birga dasturning 3 ta o'rnatilishini amalga oshiramiz.

Eslatma. Eslatib o'tamiz, bizda versiya ma'lumotlar bazasi mavjud v1. U ustunlarni o'z ichiga oladi first_name и last_name. Biz o'zgartirishimiz kerak last_name haqida surname. Bizda ilova versiyasi ham bor 1.0.0, bu hali ishlatilmagan surname.

2-qadam: Familiyani qo'shing

Ilova versiyasi: 2.0.0
JB versiyasi: v2

izoh

Yangi ustun qo'shish va uning mazmunini nusxalash orqali biz orqaga qarab mos keladigan ma'lumotlar bazasi o'zgarishlarini yaratamiz. Shu bilan birga, agar biz JARni orqaga qaytarsak yoki eski JAR ishlayotgan bo'lsa, u bajarilish vaqtida buzilmaydi.

Biz yangi versiyani chiqarmoqdamiz

Qadamlar:

  1. yangi ustun yaratish uchun ma'lumotlar bazasi migratsiyasini amalga oshiring surname. Endi sizning DB versiyangiz v2
  2. dan ma'lumotlarni nusxalash last_name в surname. E'tibor beringAgar sizda bu ma'lumotlar juda ko'p bo'lsa, siz ommaviy migratsiya haqida o'ylashingiz kerak!
  3. ular ishlatiladigan kodni yozing Ikkalasi и новыйva qadimgi ustun. Endi sizning ilovangiz versiyasi 2.0.0
  4. ustundan qiymatni o'qing surname, agar shunday bo'lmasa null, yoki l danast_name, agar surname ko'rsatilmagan. Siz o'chirishingiz mumkin getLastName() koddan, chunki u chiqadi null ilovangizni orqaga qaytarishda 3.0.0 uchun 2.0.0.

Agar siz Spring Boot Flyway dan foydalansangiz, ushbu ikki qadam versiyani ishga tushirish vaqtida amalga oshiriladi 2.0.0 ilovalar. Agar siz ma'lumotlar bazasi versiyasini yaratish vositasini qo'lda ishlatsangiz, buning uchun ikki xil ishni bajarishingiz kerak bo'ladi (avval JB versiyasini qo'lda yangilang, so'ngra yangi ilovani o'rnating).

Bu juda muhim. Yangi yaratilgan ustunni unutmang MAJBUR EMAS bo'lish NULL Emas. Agar siz orqaga qaytarishni qilsangiz, eski ilova yangi ustun haqida bilmaydi va uni o'rnatmaydi Insert. Ammo agar siz ushbu cheklovni qo'shsangiz va sizning JB bo'ladi v2, bu yangi ustunning qiymatini o'rnatishni talab qiladi. Bu cheklovlarning buzilishiga olib keladi.

Bu juda muhim. Siz usulni olib tashlashingiz kerak getLastName(), chunki versiyada 3.0.0 Kodda ustun tushunchasi yo'q last_name. Bu shuni anglatadiki, u erda null o'rnatiladi. Siz usulni qoldirib, cheklar qo'shishingiz mumkin null, lekin mantiqda bunga ishonch hosil qilish juda yaxshi yechim bo'ladi getSurname() nolga teng bo'lmagan to'g'ri qiymatni tanladingiz.

A/B testi

Hozirgi vaziyat shundan iboratki, bizda dastur versiyasi mavjud 1.0.0, ishlab chiqarishda joylashtirilgan va ma'lumotlar bazasi v1. Biz versiya ilovasining ikkinchi nusxasini joylashtirishimiz kerak 2.0.0ma'lumotlar bazasini yangilaydi v2.

Qadamlar:

  1. versiya ilovasining yangi nusxasi o'rnatildi 2.0.0ma'lumotlar bazasini yangilaydi v2
  2. ayni paytda ba'zi so'rovlar versiya misollari tomonidan qayta ishlandi 1.0.0
  3. yangilanish muvaffaqiyatli bo'ldi va sizda versiya ilovasining bir nechta ishlayotgan namunalari mavjud 1.0.0 va boshqa versiyalar 2.0.0. Har bir inson ma'lumotlar bazasi bilan aloqa qiladi v2
  4. versiyasi 1.0.0 ma'lumotlar bazasida familiya ustunini ishlatmaydi, lekin versiya 2.0.0 foydalanadi. Ular bir-biriga aralashmaydi va hech qanday xato bo'lmasligi kerak.
  5. versiyasi 2.0.0 ma'lumotlarni eski va yangi ustunda saqlaydi va orqaga qarab muvofiqlikni ta'minlaydi

Bu juda muhim. Agar sizda eski/yangi ustundagi qiymatlar asosida elementlarni hisoblaydigan so'rovlaringiz bo'lsa, sizda hozirda takroriy qiymatlar mavjudligini yodda tutishingiz kerak (ehtimol ular hali ham ko'chib o'tmoqda). Misol uchun, agar siz familiyasi (ustun qanday nomlanishidan qat'iy nazar) harf bilan boshlangan foydalanuvchilar sonini hisoblamoqchi bo'lsangiz. A, keyin ma'lumotlarni ko'chirish tugallanmaguncha (oldnew ustun) yangi ustunni so'rasangiz, nomuvofiq ma'lumotlarga ega bo'lishingiz mumkin.

Ilovani orqaga qaytarish

Endi bizda ilova versiyasi mavjud 2.0.0 va ma'lumotlar bazasi v2.

Qadamlar:

  1. ilovangizni versiyaga qaytaring 1.0.0.
  2. versiyasi 1.0.0 ma'lumotlar bazasida ustundan foydalanmaydi surname, shuning uchun orqaga qaytarish muvaffaqiyatli bo'lishi kerak

JB o'zgaradi

Ma'lumotlar bazasida nomli ustun mavjud last_name.

Flyway manba 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 qo'shing surname.

Diqqat. Yodda tutingki, siz qo'shayotgan ustunga hech qanday NOT NULL cheklovlarni QO'SHISH MUMKIN. Agar siz JAR-ni orqaga qaytarsangiz, eski versiya qo'shilgan ustun haqida hech qanday tasavvurga ega bo'lmaydi va uni avtomatik ravishda NULL ga o'rnatadi. Agar bunday cheklov mavjud bo'lsa, eski dastur shunchaki buziladi.

-- 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 o'zgaradi

Biz ma'lumotlarni shunday saqlaymiz last_name, va surname. Shu bilan birga biz o'qiymiz last_name, chunki bu ustun eng dolzarb hisoblanadi. Joylashtirish jarayonida ba'zi so'rovlar hali yangilanmagan dastur namunasi tomonidan qayta ishlangan bo'lishi mumkin.

/*
 * 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-qadam: Koddan familiyani olib tashlash

Ilova versiyasi: 3.0.0

JB versiyasi:v3

izoh

Eslatma per.: Ko'rinishidan, asl maqolada muallif ushbu blok matnini 2-bosqichdan noto'g'ri ko'chirib olgan. Ushbu bosqichda dastur kodiga ustundan foydalanadigan funksiyalarni olib tashlashga qaratilgan o'zgartirishlar kiritilishi kerak. last_name.

Yangi ustun qo'shish va uning mazmunini nusxalash orqali biz orqaga qarab mos keladigan ma'lumotlar bazasi o'zgarishlarini yaratdik. Bundan tashqari, agar biz JARni orqaga qaytarsak yoki eski JAR ishlayotgan bo'lsa, u bajarilish vaqtida buzilmaydi.

Ilovani orqaga qaytarish

Hozirda bizda ilova versiyasi mavjud 3.0.0 va ma'lumotlar bazasi v3. Versiya 3.0.0 ma’lumotlarni saqlamaydi last_name. Bu shuni anglatadiki, ichida surname eng dolzarb ma'lumotlar saqlanadi.

Qadamlar:

  1. ilovangizni versiyaga qaytaring 2.0.0.
  2. versiyasi 2.0.0 foydalanadi va last_name и surname.
  3. versiyasi 2.0.0 oladi surname, agar u nol bo'lmasa, aks holda -last_name

Ma'lumotlar bazasi o'zgarishi

Ma'lumotlar bazasida tarkibiy o'zgarishlar yo'q. Eski ma'lumotlarning yakuniy migratsiyasini amalga oshirish uchun quyidagi skript bajariladi:

-- 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 o'zgaradi

Eslatma per.: Ushbu blokning tavsifi ham muallif tomonidan 2-bosqichdan noto'g'ri ko'chirilgan. Maqola mantig'iga ko'ra, ushbu bosqichdagi koddagi o'zgartirishlar undan ustun bilan ishlaydigan elementlarni olib tashlashga qaratilgan bo'lishi kerak. last_name.

Biz ma'lumotlarni shunday saqlaymiz last_name, va surname. Bundan tashqari, biz ustundan o'qiymiz last_name, chunki u eng dolzarb hisoblanadi. Joylashtirish jarayonida ba'zi so'rovlar hali yangilanmagan namuna tomonidan qayta ishlanishi mumkin.

/*
 * 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-qadam: familiyani ma'lumotlar bazasidan olib tashlash

Ilova versiyasi: 4.0.0

JB versiyasi: v4

izoh

Versiya kodi tufayli 3.0.0 ustundan foydalanmadi last_name, agar biz orqaga qaytsak, ijro davomida hech qanday yomon narsa bo'lmaydi 3.0.0 ma'lumotlar bazasidan ustunni olib tashlaganingizdan so'ng.

Skriptni bajarish jurnallari

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

JB o'zgaradi

Nisbatan v3 biz faqat ustunni olib tashlaymiz last_name va etishmayotgan cheklovlarni qo'shing.

-- 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 o'zgaradi

Kodga hech qanday o'zgartirish kiritilmagan.

xulosa

Biz bir nechta orqaga mos keluvchi joylashtirishni amalga oshirish orqali ustun nomini orqaga mos kelmaydigan o‘zgartirishni muvaffaqiyatli qo‘lladik. Quyida bajarilgan harakatlarning qisqacha mazmuni keltirilgan:

  1. dastur versiyasini joylashtirish 1.0.0 с v1 ma'lumotlar bazasi sxemasi (ustun nomi = last_name)
  2. dastur versiyasini joylashtirish 2.0.0, ma'lumotlarni saqlaydigan last_name и surname. Ilova dan o'qiydi last_name. Ma'lumotlar bazasi versiyada v2kabi ustunlarni o'z ichiga oladi last_name, va surname. surname l nusxasi hisoblanadiast_name. (Izoh: Bu ustunda null bo'lmagan cheklov bo'lmasligi kerak)
  3. dastur versiyasini joylashtirish 3.0.0, bu faqat ma'lumotlarni saqlaydi surname va familiyadan o'qiydi. Ma'lumotlar bazasiga kelsak, oxirgi migratsiya amalga oshirilmoqda last_name в surname. Shuningdek, cheklov NULL Emas dan olib tashlandi last_name. Ma'lumotlar bazasi hozir versiyada v3
  4. dastur versiyasini joylashtirish 4.0.0 - kodga hech qanday o'zgartirish kiritilmaydi. Ma'lumotlar bazasini joylashtirish v4, olib tashlaydigan last_name. Bu erda siz ma'lumotlar bazasiga etishmayotgan cheklovlarni qo'shishingiz mumkin.

Ushbu yondashuvga rioya qilish orqali siz har doim ma'lumotlar bazasi/ilova mosligini buzmasdan bitta versiyani orqaga qaytarishingiz mumkin.

da

Ushbu maqolada ishlatiladigan barcha kodlar mavjud Github. Quyida qo'shimcha tavsif mavjud.

Loyihalar

Repozitariyni klonlashdan so'ng siz quyidagi papka tuzilishini ko'rasiz.

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

Skriptlar

Siz quyida keltirilgan skriptlarda tasvirlangan skriptlarni ishga tushirishingiz mumkin, ular ma'lumotlar bazasiga orqaga qarab mos keladigan va mos kelmaydigan o'zgarishlarni ko'rsatadi.

Ko'rish uchun orqaga qarab mos keladigan o'zgarishlar bilan ish, ishga tushirish:

./scripts/scenario_backward_compatible.sh

Va ko'rish uchun orqaga mos kelmaydigan o'zgarishlarga ega bo'lgan holat, ishga tushirish:

./scripts/scenario_backward_incompatible.sh

Bahorgi poyabzal namunasi Flyway

Barcha misollar dan olingan Spring Boot Sample Flyway.

Siz ko'rib chiqishingiz mumkin http://localhost:8080/flyway, skriptlar ro'yxati mavjud.

Ushbu misol H2 konsolini ham o'z ichiga oladi (at http://localhost:8080/h2-console) ma'lumotlar bazasi holatini ko'rishingiz mumkin (birlamchi jdbc URL manzili jdbc:h2:mem:testdb).

qo'shimcha ravishda

Shuningdek, bizning blogimizdagi boshqa maqolalarni o'qing:

Manba: www.habr.com

a Izoh qo'shish