Artikel ini menjelaskan secara detail cara mengatasi masalah kompatibilitas database dalam penyebaran. Kami akan memberi tahu Anda apa yang dapat terjadi pada aplikasi produksi Anda jika Anda mencoba menerapkannya tanpa persiapan awal. Kami kemudian akan melalui tahapan siklus hidup aplikasi yang harus memiliki waktu henti nol (kira-kira. jalur: selanjutnya - tidak ada waktu henti). Hasil dari operasi kami adalah menerapkan perubahan database yang tidak kompatibel ke belakang dengan cara yang kompatibel ke belakang.
Jika Anda ingin memahami contoh kode dari artikel, Anda dapat menemukannya di GitHub.
pengenalan
Penerapan tanpa waktu henti
Sungguh mistis penerapan tanpa downtime? Bisa dibilang ini adalah saat aplikasi Anda disebarkan sedemikian rupa sehingga Anda berhasil memperkenalkan versi baru aplikasi ke produksi, sementara pengguna tidak menyadari ketidaktersediaannya. Dari sudut pandang pengguna dan perusahaan, ini adalah skenario penerapan terbaik karena memungkinkan fitur-fitur baru diperkenalkan dan bug diperbaiki tanpa gangguan.
Bagaimana cara mencapainya? Ada beberapa cara, ini salah satunya:
menyebarkan versi No. 1 layanan Anda
melakukan migrasi basis data
Terapkan versi #2 layanan Anda secara paralel dengan versi #1
segera setelah Anda melihat bahwa versi No. 2 berfungsi sebagaimana mestinya, hapus versi No. 1
siap!
Mudah, bukan? Sayangnya, ini tidak sesederhana itu, dan kita akan membahasnya secara mendetail nanti. Sekarang mari kita periksa proses penerapan lain yang cukup umum - penerapan biru-hijau.
Pernahkah Anda mendengar penyebaran biru hijau? Cloud Foundry membuat ini sangat mudah. Lihat saja artikel ini, di mana kami menjelaskannya secara lebih rinci. Untuk meringkas secara singkat, izinkan kami mengingatkan Anda bagaimana melakukan penerapan biru hijau:
memastikan bahwa dua salinan kode produksi Anda (“biru” dan “hijau”) berfungsi;
mengarahkan semua lalu lintas ke lingkungan biru, mis. sehingga URL produksi mengarah ke sana;
menyebarkan dan menguji semua perubahan aplikasi di lingkungan hijau;
alihkan url dari lingkungan biru ke hijau
Penerapan biru hijau adalah pendekatan yang memungkinkan Anda memperkenalkan fitur baru dengan mudah tanpa khawatir akan gangguan produksi. Hal ini disebabkan oleh fakta bahwa meskipun terjadi sesuatu, Anda dapat dengan mudah kembali ke lingkungan sebelumnya hanya dengan “menjentikkan tombol”.
Setelah membaca semua hal di atas, Anda mungkin bertanya pertanyaan: Apa hubungannya zero downtime dengan penerapan Blue green?
Ya, keduanya memiliki banyak kesamaan, karena memelihara dua salinan dari lingkungan yang sama memerlukan upaya dua kali lipat untuk memeliharanya. Inilah sebabnya mengapa beberapa tim mengklaim Martin Fowler, ikuti variasi pendekatan ini:
Pilihan lainnya adalah menggunakan database yang sama, membuat saklar biru-hijau untuk lapisan web dan domain. Dalam pendekatan ini, database sering kali menjadi masalah, terutama ketika Anda perlu mengubah skemanya untuk mendukung perangkat lunak versi baru.
Dan di sinilah kita sampai pada masalah utama dalam artikel ini. Basis data. Mari kita lihat lagi ungkapan ini.
melakukan migrasi basis data.
Sekarang Anda harus bertanya pada diri sendiri pertanyaan - bagaimana jika perubahan database tidak kompatibel? Apakah versi pertama aplikasi saya tidak akan rusak? Faktanya, inilah yang akan terjadi...
Jadi, meskipun penerapan zero downtime/blue green memberikan manfaat yang sangat besar, perusahaan cenderung mengikuti proses yang lebih aman berikut ini dalam menerapkan aplikasi mereka:
siapkan paket dengan aplikasi versi baru
mematikan aplikasi yang sedang berjalan
menjalankan skrip untuk memigrasikan database
menyebarkan dan meluncurkan versi baru aplikasi
Dalam artikel ini, kami akan menjelaskan secara rinci bagaimana Anda dapat bekerja dengan database dan kode Anda untuk memanfaatkan penerapan zero downtime.
Masalah basis data
Jika Anda memiliki aplikasi tanpa kewarganegaraan yang tidak menyimpan data apa pun di database, Anda bisa langsung mendapatkan penerapan zero downtime. Sayangnya, sebagian besar perangkat lunak perlu menyimpan data di suatu tempat. Inilah sebabnya mengapa Anda harus berpikir dua kali sebelum melakukan perubahan apa pun pada sirkuit. Sebelum kita membahas detail tentang cara mengubah skema sehingga penerapan tanpa downtime dapat dilakukan, pertama-tama mari kita fokus pada skema pembuatan versi.
Skema pembuatan versi
Pada artikel ini kita akan menggunakan jalur terbang sebagai alat kontrol versi (kira-kira. Terjemahan: kita berbicara tentang migrasi basis data). Biasanya, kami juga akan menulis aplikasi Spring Boot yang memiliki dukungan Flyway bawaan dan akan melakukan migrasi skema saat menyiapkan konteks aplikasi. Saat menggunakan Flyway, Anda dapat menyimpan skrip migrasi di folder proyek Anda (secara default di classpath:db/migration). Di sini Anda dapat melihat contoh file migrasi tersebut
Dalam contoh ini kita melihat 4 skrip migrasi yang, jika tidak dijalankan sebelumnya, akan dieksekusi satu demi satu saat aplikasi dimulai. Mari kita lihat salah satu file (V1__init.sql) sebagai contoh.
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');
Semuanya cukup jelas: Anda dapat menggunakan SQL untuk menentukan bagaimana database Anda harus dimodifikasi. Untuk informasi lebih lanjut tentang Spring Boot dan Flyway, lihat Dokumen Boot Musim Semi.
Dengan menggunakan alat kontrol sumber dengan Spring Boot, Anda mendapatkan 2 manfaat besar:
Anda memisahkan perubahan basis data dari perubahan kode
Migrasi basis data terjadi bersamaan dengan peluncuran aplikasi Anda, mis. proses penerapan Anda disederhanakan
Memecahkan masalah basis data
Di bagian artikel selanjutnya, kita akan fokus melihat dua pendekatan terhadap perubahan database.
ketidakcocokan ke belakang
kompatibilitas terbalik
Yang pertama akan dianggap sebagai peringatan bahwa Anda tidak boleh melakukan penerapan zero downtime tanpa persiapan awal... Yang kedua menawarkan solusi tentang bagaimana Anda dapat melakukan penerapan tanpa downtime dan pada saat yang sama menjaga kompatibilitas ke belakang.
Proyek yang akan kami kerjakan adalah aplikasi Spring Boot Flyway sederhana yang memiliki Person с first_name и last_name dalam basis data (kira-kira. terjemahan: Person adalah tabel dan first_name и last_name - ini adalah bidang di dalamnya). Kami ingin mengganti nama last_name в surname.
Asumsi
Sebelum kita membahas detailnya, ada beberapa asumsi yang perlu kita buat tentang aplikasi kita. Hasil utama yang ingin kami capai adalah proses yang cukup sederhana.
Catatan. TIPS PRO Bisnis. Menyederhanakan proses dapat menghemat banyak uang untuk dukungan (semakin banyak orang yang bekerja untuk perusahaan Anda, semakin banyak uang yang dapat Anda hemat)!
Tidak perlu mengembalikan database
Ini menyederhanakan proses penerapan (beberapa rollback database hampir tidak mungkin dilakukan, seperti rollback penghapusan). Kami lebih memilih untuk mengembalikan aplikasi saja. Dengan cara ini, meskipun Anda memiliki database yang berbeda (misalnya, SQL dan NoSQL), alur penerapan Anda akan terlihat sama.
Aplikasi harus SELALU dapat diputar kembali satu versi ke belakang (tidak lebih)
Rollback sebaiknya hanya dilakukan bila diperlukan. Jika ada bug di versi saat ini yang tidak mudah diperbaiki, kami harus dapat kembali ke versi terbaru yang berfungsi. Kami berasumsi bahwa versi terbaru yang berfungsi ini adalah versi sebelumnya. Mempertahankan kompatibilitas kode dan database untuk lebih dari satu peluncuran akan sangat sulit dan mahal.
Catatan. Agar lebih mudah dibaca, dalam artikel ini kami akan mengubah versi utama aplikasi.
Langkah 1: Keadaan Awal
Versi aplikasi: 1.0.0
Versi DB: v1
Komentar
Ini akan menjadi keadaan awal aplikasi.
Perubahan basis data
DB berisi 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');
Perubahan kode
Aplikasi menyimpan data Orang di 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
+ "]";
}
}
Penggantian nama kolom mundur tidak kompatibel
Mari kita lihat contoh cara mengubah nama kolom:
Perhatian. Contoh berikut dengan sengaja akan merusak sesuatu. Kami menunjukkan ini untuk menunjukkan masalah kompatibilitas database.
Versi aplikasi: 2.0.0.BAD
Versi DB: v2bad
Komentar
Perubahan saat ini TIDAK mengizinkan kami menjalankan dua instance (lama dan baru) secara bersamaan. Dengan demikian, penerapan zero downtime akan sulit dicapai (jika asumsi diperhitungkan, hal ini sebenarnya tidak mungkin).
pengujian A/B
Situasi saat ini adalah kami memiliki versi aplikasi 1.0.0, digunakan dalam produksi, dan database v1. Kita perlu menyebarkan aplikasi kedua, version 2.0.0.BAD, dan perbarui database ke v2bad.
Langkah-langkah:
contoh baru dari aplikasi versi dikerahkan 2.0.0.BADyang memperbarui database v2bad
dalam database v2bad kolom last_name tidak ada lagi - telah diubah menjadi surname
Pembaruan database dan aplikasi berhasil dan beberapa instance berjalan 1.0.0, lainnya - masuk 2.0.0.BAD. Semuanya terhubung ke database v2bad
semua contoh versi 1.0.0 akan mulai membuat kesalahan karena mereka akan mencoba memasukkan data ke dalam kolom last_nameyang sudah tidak ada lagi
semua contoh versi 2.0.0.BAD akan bekerja tanpa masalah
Seperti yang Anda lihat, jika kami membuat perubahan yang tidak kompatibel pada database dan aplikasi, pengujian A/B tidak mungkin dilakukan.
Pengembalian aplikasi
Mari kita asumsikan bahwa setelah mencoba melakukan penerapan A/B (kira-kira. per.: yang penulis maksud mungkin adalah pengujian A/B di sini) kami memutuskan bahwa kami perlu mengembalikan aplikasi ke versinya 1.0.0. Katakanlah kita tidak ingin melakukan rollback database.
Langkah-langkah:
kami menghentikan contoh aplikasi versi 2.0.0.BAD
databasenya masih ada v2bad
sejak versinya 1.0.0 tidak mengerti apa itu surname, kita akan melihat kesalahan
neraka sudah pecah, kita tidak bisa kembali lagi
Seperti yang Anda lihat, jika kami membuat perubahan yang tidak kompatibel pada database dan aplikasi, kami tidak dapat melakukan roll back ke versi sebelumnya.
Log eksekusi skrip
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"}
Perubahan basis data
Skrip migrasi yang mengganti nama last_name в surname
Skrip Sumber Jalur Terbang:
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');
Skrip yang mengganti nama last_name.
-- This change is backward incompatible - you can't do A/B testing
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;
Perubahan kode
Kami telah mengubah nama bidang lastName pada surname.
Mengganti nama kolom dengan cara yang kompatibel dengan versi sebelumnya
Ini adalah situasi paling umum yang mungkin kita temui. Kita perlu melakukan perubahan yang tidak sejalan dengan masa lalu. Kami telah membuktikan bahwa untuk penerapan zero-downtime, kami tidak boleh hanya menerapkan migrasi database tanpa langkah tambahan. Di bagian artikel ini, kami akan melakukan 3 penerapan aplikasi bersama dengan migrasi database untuk mencapai hasil yang diinginkan dengan tetap menjaga kompatibilitas ke belakang.
Catatan. Ingatlah bahwa kami memiliki database versi v1. Ini berisi kolom first_name и last_name. Kita harus berubah last_name pada surname. Kami juga memiliki versi aplikasi 1.0.0, yang belum digunakan surname.
Langkah 2: Tambahkan nama keluarga
Versi aplikasi: 2.0.0
Versi DB: v2
Komentar
Dengan menambahkan kolom baru dan menyalin isinya, kami membuat perubahan database yang kompatibel. Pada saat yang sama, jika kita melakukan rollback JAR atau menjalankan JAR lama, JAR tersebut tidak akan rusak selama eksekusi.
Kami meluncurkan versi baru
Langkah-langkah:
melakukan migrasi database untuk membuat kolom baru surname. Sekarang versi DB Anda v2
menyalin data dari last_name в surname. Catatanbahwa jika Anda memiliki banyak data ini, Anda harus mempertimbangkan migrasi batch!
tulis kode di mana mereka digunakan KEDUA и baruDan yang lama kolom. Sekarang versi aplikasi Anda 2.0.0
membaca nilai dari kolom surname, Jika tidak null, atau dari last_name, jika surname tidak ditentukan. Anda dapat menghapus getLastName() dari kode, karena akan ditampilkan null saat mengembalikan aplikasi Anda dari 3.0.0 untuk 2.0.0.
Jika Anda menggunakan Spring Boot Flyway, kedua langkah ini akan dilakukan selama permulaan versi 2.0.0 aplikasi. Jika Anda menjalankan alat pembuatan versi database secara manual, Anda harus melakukan dua hal berbeda untuk melakukannya (pertama perbarui versi db secara manual, lalu terapkan aplikasi baru).
Itu penting. Ingat kolom yang baru dibuat JANGAN menjadi TIDAK NIHIL. Jika Anda melakukan rollback, aplikasi lama tidak mengetahui kolom baru dan tidak akan menginstalnya selama Insert. Tetapi jika Anda menambahkan batasan ini dan db Anda akan menjadi v2, ini memerlukan pengaturan nilai kolom baru. Yang akan mengakibatkan pelanggaran pembatasan.
Itu penting. Anda harus menghapus metode ini getLastName(), karena dalam versi 3.0.0 Tidak ada konsep kolom dalam kode last_name. Ini berarti null akan disetel di sana. Anda dapat meninggalkan metode ini dan menambahkan cek null, tetapi solusi yang jauh lebih baik adalah memastikan hal itu masuk akal getSurname() Anda memilih nilai bukan nol yang benar.
pengujian A/B
Situasi saat ini adalah kami memiliki versi aplikasi 1.0.0, diterapkan pada produksi, dan database di v1. Kita perlu menerapkan contoh kedua dari aplikasi versi 2.0.0yang akan memperbarui database menjadi v2.
Langkah-langkah:
contoh baru dari aplikasi versi dikerahkan 2.0.0yang memperbarui database v2
sementara itu beberapa permintaan diproses oleh instance versi 1.0.0
pembaruan berhasil dan Anda memiliki beberapa contoh aplikasi versi yang berjalan 1.0.0 dan versi lainnya 2.0.0. Semua orang berkomunikasi dengan database di v2
versi 1.0.0 tidak menggunakan kolom nama keluarga di database, melainkan versinya 2.0.0 kegunaan. Mereka tidak saling mengganggu, dan tidak boleh ada kesalahan.
versi 2.0.0 menyimpan data di kolom lama dan baru, memastikan kompatibilitas ke belakang
Itu penting. Jika Anda memiliki pertanyaan yang menghitung item berdasarkan nilai dari kolom lama/baru, Anda harus ingat bahwa Anda sekarang memiliki nilai duplikat (kemungkinan besar nilai tersebut masih bermigrasi). Misalnya, jika Anda ingin menghitung jumlah pengguna yang nama belakangnya (apa pun nama kolomnya) diawali dengan huruf A, lalu hingga migrasi data selesai (old → new kolom) Anda mungkin memiliki data yang tidak konsisten jika Anda menanyakan kolom baru.
Pengembalian aplikasi
Sekarang kami memiliki versi aplikasi 2.0.0 dan basis data di v2.
Langkah-langkah:
kembalikan aplikasi Anda ke versi 1.0.0.
versi 1.0.0 tidak menggunakan kolom dalam database surname, jadi rollback seharusnya berhasil
Perubahan DB
Basis data berisi kolom bernama last_name.
Skrip sumber jalur terbang:
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');
Tambahkan skrip surname.
Perhatian. Ingatlah bahwa Anda TIDAK BISA MENAMBAHKAN batasan NOT NULL apa pun ke kolom yang Anda tambahkan. Jika Anda mengembalikan JAR, versi lama tidak akan mengetahui kolom yang ditambahkan dan secara otomatis akan menyetelnya ke NULL. Jika ada batasan seperti itu, aplikasi lama akan rusak begitu saja.
-- 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
Perubahan kode
Kami menyimpan data sebagai last_name, dan masuk surname. Pada saat yang sama kita membaca dari last_name, karena kolom ini adalah yang paling relevan. Selama proses penerapan, beberapa permintaan mungkin telah diproses oleh instance aplikasi yang belum diperbarui.
/*
* 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
+ "]";
}
}
Langkah 3: Menghapus nama_belakang dari kode
Versi aplikasi: 3.0.0
Versi DB:v3
Komentar
Catatan per.: Rupanya, di artikel asli penulis salah menyalin teks blok ini dari langkah 2. Pada langkah ini, perubahan harus dilakukan pada kode aplikasi yang bertujuan untuk menghilangkan fungsionalitas yang menggunakan kolom last_name.
Dengan menambahkan kolom baru dan menyalin isinya, kami membuat perubahan database yang kompatibel. Selain itu, jika kita melakukan rollback JAR atau menjalankan JAR lama, JAR tidak akan rusak selama eksekusi.
Pengembalian aplikasi
Saat ini kami memiliki versi aplikasi 3.0.0 dan basis data v3. Versi: kapan 3.0.0 tidak menyimpan data ke last_name. Artinya di surname informasi terkini disimpan.
Langkah-langkah:
kembalikan aplikasi Anda ke versi 2.0.0.
versi 2.0.0 kegunaan dan last_name и surname.
versi 2.0.0 akan mengambil surname, jika bukan nol, jika tidak -last_name
Perubahan basis data
Tidak ada perubahan struktural dalam database. Skrip berikut dijalankan untuk melakukan migrasi terakhir dari data lama:
-- 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;
Perubahan kode
Catatan per.: Deskripsi blok ini juga secara keliru disalin oleh penulis dari langkah 2. Sesuai dengan logika artikel, perubahan kode pada langkah ini harus ditujukan untuk menghilangkan elemen-elemen yang berfungsi dengan kolom tersebut. last_name.
Kami menyimpan data sebagai last_name, dan masuk surname. Selain itu, kami membaca dari kolom last_name, karena ini yang paling relevan. Selama proses penerapan, beberapa permintaan mungkin diproses oleh instans yang belum diupgrade.
/*
* 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
+ "]";
}
}
Langkah 4: Menghapus nama_belakang dari database
Versi aplikasi: 4.0.0
Versi DB: v4
Komentar
Karena kenyataan bahwa kode versi 3.0.0 tidak menggunakan kolom last_name, tidak ada hal buruk yang akan terjadi selama eksekusi jika kita memutar kembali 3.0.0 setelah menghapus kolom dari database.
Log eksekusi skrip
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"}
Perubahan DB
Relatif v3 kita hapus saja kolomnya last_name dan menambahkan batasan yang hilang.
-- 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;
Perubahan kode
Tidak ada perubahan pada kode.
Keluaran
Kami berhasil menerapkan perubahan nama kolom yang tidak kompatibel dengan melakukan beberapa penerapan yang kompatibel dengan versi sebelumnya. Di bawah ini adalah ringkasan tindakan yang dilakukan:
penyebaran versi aplikasi 1.0.0 с v1 skema database (nama kolom = last_name)
penyebaran versi aplikasi 2.0.0, yang menyimpan data di dalamnya last_name и surname. Aplikasi membaca dari last_name. Basis data ada dalam versi v2berisi kolom seperti last_nameDan surname. surname adalah salinan last_name. (CATATAN: Kolom ini tidak boleh memiliki batasan bukan nol)
penyebaran versi aplikasi 3.0.0, yang hanya menyimpan data surname dan membaca dari nama keluarga. Sedangkan untuk database, migrasi terakhir sedang berlangsung last_name в surname. Juga sebuah batasan TIDAK NIHIL dihapus dari last_name. Basis data sekarang dalam versi v3
penyebaran versi aplikasi 4.0.0 - tidak ada perubahan yang dilakukan pada kode. Penyebaran basis data v4, yang menghapus last_name. Di sini Anda dapat menambahkan batasan yang hilang ke database.
Dengan mengikuti pendekatan ini, Anda selalu dapat melakukan roll back satu versi tanpa merusak kompatibilitas database/aplikasi.
kode
Semua kode yang digunakan dalam artikel ini tersedia di Github. Di bawah ini adalah penjelasan tambahan.
proyek
Setelah mengkloning repositori, Anda akan melihat struktur folder berikut.
├── 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)
Skrip
Anda dapat menjalankan skrip yang dijelaskan dalam skrip di bawah ini, yang akan menunjukkan perubahan yang kompatibel ke belakang dan tidak kompatibel pada database.
Untuk melihat kasus dengan perubahan yang kompatibel ke belakang, berlari:
./scripts/scenario_backward_compatible.sh
Dan untuk melihat kasus dengan perubahan yang tidak kompatibel ke belakang, berlari:
./scripts/scenario_backward_incompatible.sh
Contoh Jalur Terbang Spring Boot
Semua contoh diambil dari Spring Boot Sample Flyway.
Anda dapat melihatnya http://localhost:8080/flyway, ada daftar skrip.
Contoh ini juga mencakup konsol H2 (at http://localhost:8080/h2-console) sehingga Anda dapat melihat status database (URL jdbc default adalah jdbc:h2:mem:testdb).