Penerapan dan Basis Data Tanpa Waktu Henti

Penerapan dan Basis Data Tanpa Waktu Henti

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

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

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:

  1. contoh baru dari aplikasi versi dikerahkan 2.0.0.BADyang memperbarui database v2bad
  2. dalam database v2bad kolom last_name tidak ada lagi - telah diubah menjadi surname
  3. Pembaruan database dan aplikasi berhasil dan beberapa instance berjalan 1.0.0, lainnya - masuk 2.0.0.BAD. Semuanya terhubung ke database v2bad
  4. 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
  5. 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:

  1. kami menghentikan contoh aplikasi versi 2.0.0.BAD
  2. databasenya masih ada v2bad
  3. sejak versinya 1.0.0 tidak mengerti apa itu surname, kita akan melihat kesalahan
  4. 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:

  1. melakukan migrasi database untuk membuat kolom baru surname. Sekarang versi DB Anda v2
  2. menyalin data dari last_name в surname. Catatanbahwa jika Anda memiliki banyak data ini, Anda harus mempertimbangkan migrasi batch!
  3. tulis kode di mana mereka digunakan KEDUA и baruDan yang lama kolom. Sekarang versi aplikasi Anda 2.0.0
  4. 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:

  1. contoh baru dari aplikasi versi dikerahkan 2.0.0yang memperbarui database v2
  2. sementara itu beberapa permintaan diproses oleh instance versi 1.0.0
  3. 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
  4. 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.
  5. 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 (oldnew 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:

  1. kembalikan aplikasi Anda ke versi 1.0.0.
  2. 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:

  1. kembalikan aplikasi Anda ke versi 2.0.0.
  2. versi 2.0.0 kegunaan dan last_name и surname.
  3. 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:

  1. penyebaran versi aplikasi 1.0.0 с v1 skema database (nama kolom = last_name)
  2. 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)
  3. 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
  4. 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).

tambahan

Baca juga artikel lainnya di blog kami:

Sumber: www.habr.com

Tambah komentar