れロダりンタむムの導入ずデヌタベヌス

れロダりンタむムの導入ずデヌタベヌス

この蚘事では、展開時のデヌタベヌス互換性の問題を解決する方法に぀いお詳しく説明したす。 事前の準備をせずに導入しようずするず、運甚アプリケヌションに䜕が起こる可胜性があるかを説明したす。 次に、ダりンタむムをれロにするために必芁なアプリケヌション ラむフサむクルの段階を芋おいきたす (玄。 レヌン: さらに - ダりンタむムれロ。 私たちの操䜜の結果、䞋䜍互換性のないデヌタベヌスの倉曎が䞋䜍互換性のある方法で適甚されたす。

この蚘事のコヌド䟋を理解したい堎合は、次の堎所で芋぀けるこずができたす。 GitHubの.

導入

ダりンタむムれロの導入

なんお神秘的なんだろう ダりンタむムれロの導入? これは、アプリケヌションの新しいバヌゞョンを運甚環境に正垞に導入できる方法でアプリケヌションがデプロむされおいるにもかかわらず、ナヌザヌがそのアプリケヌションが利甚できないこずに気付かない状態であるず蚀えたす。 ナヌザヌず䌁業の芳点から芋るず、䞭断するこずなく新機胜を導入し、バグを修正できるため、これは可胜な限り最良の導入シナリオです。

これを達成するにはどうすればよいでしょうか? いく぀かの方法がありたすが、ここではそのうちの XNUMX ぀を玹介したす。

  • サヌビスのバヌゞョン No. 1 をデプロむしたす
  • デヌタベヌス移行を実行する
  • サヌビスのバヌゞョン #2 をバヌゞョン #1 ず䞊行しおデプロむしたす
  • バヌゞョン 2 が正垞に動䜜するこずが確認できたら、すぐにバヌゞョン 1 を削陀したす。
  • できた

簡単ですね。 残念ながら、これはそれほど単玔ではありたせん。これに぀いおは埌で詳しく説明したす。 ここで、もう XNUMX ぀の非垞に䞀般的なデプロむメント プロセスである Blue Green デプロむメントを確認しおみたしょう。

聞いたこずがありたすか ブルヌグリヌン展開? Cloud Foundry を䜿甚するず、これが非垞に簡単になりたす。 ちょっず芋おください この蚘事で、これに぀いお詳しく説明したす。 簡単に芁玄するず、Blue Green デプロむメントを実行する方法を思い出しおください。

  • 補品コヌドの XNUMX ぀のコピヌ (「青」ず「緑」) が機胜するこずを確認したす。
  • すべおのトラフィックを青い環境に転送したす。぀たり、 実皌働 URL がそこを指すようにしたす。
  • すべおのアプリケヌションの倉曎をグリヌン環境で展開しおテストしたす。
  • URLを青色の環境から緑色の環境に切り替える

ブルヌ グリヌン デプロむメントは、運甚の䞭断を心配するこずなく、新しい機胜を簡単に導入できるアプロヌチです。 それは、䜕か起こっおも「スむッチを入れる」だけで簡単に前の環境に戻すこずができるからです。

䞊蚘をすべお読んだ埌、次のような疑問が生じるかもしれたせん。れロ ダりンタむムはブルヌ グリヌン展開ずどのような関係があるのでしょうか?

同じ環境の XNUMX ぀のコピヌを維持するには、維持するのに XNUMX 倍の劎力が必芁になるため、これらには非垞に倚くの共通点がありたす。 䞀郚のチヌムが䞻匵するのはこのためです マヌティン・ファりラヌ、このアプロヌチのバリ゚ヌションに埓いたす。

もう XNUMX ぀のオプションは、同じデヌタベヌスを䜿甚しお、Web 局ずドメむン局に Blue-Green スむッチを䜜成するこずです。 このアプロヌチでは、特に゜フトりェアの新しいバヌゞョンをサポヌトするためにデヌタベヌスのスキヌマを倉曎する必芁がある堎合に、デヌタベヌスが問題になるこずがよくありたす。

そしおここでこの蚘事の最倧の問題点に到達したす。 デヌタベヌス。 この蚀葉をもう䞀床芋おみたしょう。

デヌタベヌスの移行を実行したす。

ここで、デヌタベヌスの倉曎に䞋䜍互換性がない堎合はどうなるのか、ずいう質問を自問する必芁がありたす。 アプリの最初のバヌゞョンは壊れたせんか? 実際、たさにこれが起こるこずになるのです...

したがっお、れロ ダりンタむム/ブルヌ グリヌン デプロむメントの倧きな利点にもかかわらず、䌁業はアプリケヌションをデプロむするために次のより安党なプロセスに埓う傟向がありたす。

  • アプリケヌションの新しいバヌゞョンを含むパッケヌゞを準備する
  • 実行䞭のアプリケヌションをシャットダりンする
  • スクリプトを実行しおデヌタベヌスを移行する
  • 新しいバヌゞョンのアプリケヌションをデプロむしお起動する

この蚘事では、デヌタベヌスずコヌドを操䜜しおれロ ダりンタむム展開を掻甚する方法に぀いお詳しく説明したす。

デヌタベヌスの問題

デヌタベヌスにデヌタを保存しないステヌトレス アプリケヌションがある堎合は、ダりンタむムれロの展開をすぐに実珟できたす。 残念ながら、ほずんどの゜フトりェアはデヌタをどこかに保存する必芁がありたす。 このため、回路に倉曎を加える前によく考えおください。 ダりンタむムなしの展開を可胜にするためにスキヌマを倉曎する方法の詳现に入る前に、たずバヌゞョニング スキヌマに焊点を圓おたしょう。

バヌゞョン管理スキヌム

この蚘事では、 フラむりェむ バヌゞョン管理ツヌルずしお (玄。 翻蚳: 私たちはデヌタベヌスの移行に぀いお話しおいたす。。 圓然のこずながら、Flyway サポヌトが組み蟌たれた Spring Boot アプリケヌションも䜜成し、アプリケヌション コンテキストのセットアップ䞭にスキヌマの移行を実行したす。 Flyway を䜿甚する堎合、移行スクリプトをプロゞェクト フォルダヌ (デフォルトでは classpath:db/migration。 ここでは、そのような移行ファむルの䟋を確認できたす。

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

この䟋では、事前に実行されおいない堎合、アプリケヌションの起動時に順番に実行される 4 ぀の移行スクリプトが衚瀺されたす。 ファむルの XNUMX ぀を芋おみたしょう (V1__init.sql䟋ずしお。

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

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

すべおは完党に䞀目瞭然です。SQL を䜿甚しおデヌタベヌスを倉曎する方法を定矩できたす。 Spring Boot ず Flyway の詳现に぀いおは、以䞋をご芧ください。 Spring Boot ドキュメント.

Spring Boot で゜ヌス管理ツヌルを䜿甚するず、次の 2 ぀の倧きな利点が埗られたす。

  • デヌタベヌスの倉曎ずコヌドの倉曎を分離する
  • デヌタベヌスの移行は、アプリケヌションのロヌルアりトずずもに発生したす。 導入プロセスが簡玠化されたす

デヌタベヌスの問題のトラブルシュヌティング

この蚘事の次のセクションでは、デヌタベヌス倉曎に察する XNUMX ぀のアプロヌチに焊点を圓おたす。

  • 䞋䜍非互換性
  • 䞋䜍互換性

最初のものは、事前準備なしにれロ ダりンタむム デプロむメントを実行すべきではないずいう譊告ずみなされたす。XNUMX ぀目は、ダりンタむムなしでデプロむメントを実行し、同時に䞋䜍互換性を維持する方法に関する゜リュヌションを提䟛したす。

私たちが取り組むプロゞェクトは、次のような単玔な Spring Boot Flyway アプリケヌションです。 Person с first_name О last_name デヌタベヌス内 (玄。 翻蚳 Person はテヌブルであり、first_name О last_name - これらはその䞭のフィヌルドです。 名前を倉曎したい last_name в surname.

仮定

詳现に入る前に、アプリケヌションに぀いおいく぀かの前提条件を立おる必芁がありたす。 私たちが達成したい䞻な結果は、非垞に単玔なプロセスです。

ノヌト。 ビゞネスPRO-TIP。 プロセスを簡玠化するず、サポヌトにかかる費甚を倧幅に節玄できたす (䌚瀟で働く人員が増えれば増えるほど、より倚くの費甚を節玄できたす)。

デヌタベヌスをロヌルバックする必芁はありたせん

これにより、展開プロセスが簡玠化されたす (削陀ロヌルバックなど、䞀郚のデヌタベヌスのロヌルバックはほが䞍可胜です)。 アプリケヌションのみをロヌルバックするこずを掚奚したす。 こうするこずで、異なるデヌタベヌス (SQL ず NoSQL など) がある堎合でも、デプロむメント パむプラむンは同じように芋えたす。

アプリケヌションを垞に XNUMX ぀前のバヌゞョンにロヌルバックできる必芁がありたす (それ以䞊はロヌルバックできたせん)

ロヌルバックは必芁な堎合にのみ実行しおください。 珟圚のバヌゞョンに簡単に修正できないバグがある堎合は、最新の動䜜バヌゞョンに戻すこずができるはずです。 この最新の動䜜バヌゞョンが以前のバヌゞョンであるず想定したす。 耇数のロヌルアりトに察しおコヌドずデヌタベヌスの互換性を維持するこずは非垞に困難であり、コストがかかりたす。

備考。 読みやすくするために、この蚘事ではアプリケヌションのメゞャヌ バヌゞョンを倉曎したす。

ステップ 1: 初期状態

アプリケヌションのバヌゞョン 1.0.0
DBバヌゞョン: v1

コメント

これがアプリケヌションの初期状態になりたす。

デヌタベヌスの倉曎

DBに含たれるもの last_name.

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

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

コヌドの倉曎

アプリケヌションは個人デヌタを次の堎所に保存したす。 last_name:

/*
 * Copyright 2012-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package sample.flyway;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return this.lastName;
    }

    public void setLastName(String lastname) {
        this.lastName = lastname;
    }

    @Override
    public String toString() {
        return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName
                + "]";
    }
}

䞋䜍互換性のない列名の倉曎

列名を倉曎する方法の䟋を芋おみたしょう。

泚意しおください。 次の䟋では、意図的に䜕かを壊したす。 デヌタベヌスの互換性の問題を瀺すためにこれを瀺したす。

アプリケヌションのバヌゞョン 2.0.0.BAD

DBバヌゞョン: v2bad

コメント

珟圚の倉曎では、XNUMX ぀のむンスタンス (新旧) を同時に実行するこずはできたせん。 したがっお、ダりンタむムれロの導入を達成するこずは困難です (前提条件を考慮するず、実際には䞍可胜です)。

A/B テスト

珟状ではアプリ版はありたす 1.0.0, 本番環境ずデヌタベヌスにデプロむされる v1。 アプリケヌションの XNUMX 番目のむンスタンス、バヌゞョンをデプロむする必芁がありたす。 2.0.0.BAD、デヌタベヌスを次のように曎新したす v2bad.

手順

  1. バヌゞョンアプリケヌションの新しいむンスタンスがデプロむされたす 2.0.0.BADこれによりデヌタベヌスが曎新されたす v2bad
  2. デヌタベヌス内で v2bad 桁 last_name もう存圚したせん - に倉曎されたした surname
  3. デヌタベヌスずアプリケヌションの曎新は成功し、いく぀かのむンスタンスが実行されおいたす 1.0.0、その他 - で 2.0.0.BAD。 すべおがデヌタベヌスに接続されおいる v2bad
  4. バヌゞョンのすべおのむンスタンス 1.0.0 デヌタを列に挿入しようずするため、゚ラヌがスロヌされ始めたす。 last_nameもう存圚しない人
  5. バヌゞョンのすべおのむンスタンス 2.0.0.BAD 問題なく動䜜したす

ご芧のずおり、デヌタベヌスずアプリケヌションに䞋䜍互換性のない倉曎を加えた堎合、A/B テストは䞍可胜です。

アプリケヌションのロヌルバック

A/B デプロむメントを実行しようずした埌、次のように仮定したす (玄。 per.: 著者はおそらくここで A/B テストを意味したず思われたす) アプリケヌションをそのバヌゞョンにロヌルバックする必芁があるず刀断したした。 1.0.0. デヌタベヌスをロヌルバックしたくないずしたしょう。

手順

  1. バヌゞョンアプリケヌションむンスタンスを停止したす 2.0.0.BAD
  2. デヌタベヌスはただ残っおいたす v2bad
  3. バヌゞョン以来 1.0.0 それが䜕なのか理解できたせん surname、゚ラヌが衚瀺されたす
  4. 地獄が厩壊した、もう戻るこずはできない

ご芧のずおり、デヌタベヌスずアプリケヌションに䞋䜍互換性のない倉曎を加えた堎合、以前のバヌゞョンにロヌルバックするこずはできたせん。

スクリプト実行ログ

Backward incompatible scenario:

01) Run 1.0.0
02) Wait for the app (1.0.0) to boot
03) Generate a person by calling POST localhost:9991/person to version 1.0.0
04) Run 2.0.0.BAD
05) Wait for the app (2.0.0.BAD) to boot
06) Generate a person by calling POST localhost:9991/person to version 1.0.0 <-- this should fail
07) Generate a person by calling POST localhost:9992/person to version 2.0.0.BAD <-- this should pass

Starting app in version 1.0.0
Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}

Starting app in version 2.0.0.BAD
Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

curl: (22) The requested URL returned error: 500 Internal Server Error

Generate a person in version 2.0.0.BAD
Sending a post to 127.0.0.1:9995/person. This is the response:

{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}

デヌタベヌスの倉曎

名前を倉曎する移行スクリプト last_name в surname

゜ヌス Flyway スクリプト:

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

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

名前を倉曎するスクリプト last_name.

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

コヌドの倉曎

フィヌルド名を倉曎したした lastName Ма surname.

䞋䜍互換性のある方法で列の名前を倉曎する

これは私たちが遭遇する可胜性のある最も䞀般的な状況です。 䞋䜍互換性のない倉曎を加える必芁がありたす。 ダりンタむムをれロに展開するには、远加の手順を行わずにデヌタベヌス移行を単玔に適甚すべきではないこずがすでに蚌明されおいたす。 この蚘事のこのセクションでは、䞋䜍互換性を維持しながら望たしい結果を達成するために、デヌタベヌスの移行ずずもにアプリケヌションの 3 ぀のデプロむメントを実行したす。

ノヌト。 バヌゞョン デヌタベヌスがあるこずを思い出しおください。 v1。 列が含たれおいたす first_name О last_name。 私たちは倉わらなければなりたせん last_name Ма surname。 アプリ版もありたす 1.0.0, ただ䜿甚されおいないもの surname.

ステップ 2: 姓を远加する

アプリケヌションのバヌゞョン 2.0.0
DBバヌゞョン: v2

コメント

新しい列を远加しおその内容をコピヌするこずで、䞋䜍互換性のあるデヌタベヌスの倉曎を䜜成したす。 同時に、JAR をロヌルバックしたり、叀い JAR を実行したりしおも、実行䞭に壊れるこずはありたせん。

新しいバヌゞョンを公開䞭です

手順

  1. デヌタベヌス移行を実行しお新しい列を䜜成する surname。 DBのバヌゞョンが決たりたした v2
  2. からデヌタをコピヌする last_name в surname. 泚意このデヌタが倧量にある堎合は、バッチ移行を怜蚎する必芁がありたす。
  3. 䜿甚される堎所にコヌドを曞く どちらも О 新しいず 叀い カラム。 これでアプリのバヌゞョンが 2.0.0
  4. 列から倀を読み取る surname、そうでない堎合 null、たたは l からast_nameもし surname 指定されおいない。 削陀できたす getLastName() コヌドから出力されるため、 null アプリケヌションをロヌルバックするずき 3.0.0 ЎП 2.0.0.

Spring Boot Flyway を䜿甚しおいる堎合、これらの XNUMX ぀の手順はバヌゞョンの起動時に実行されたす。 2.0.0 アプリケヌション。 デヌタベヌス バヌゞョン管理ツヌルを手動で実行する堎合、これを行うには XNUMX ぀の異なる䜜業を行う必芁がありたす (最初にデヌタベヌスのバヌゞョンを手動で曎新し、次に新しいアプリケヌションをデプロむしたす)。

それは重芁です。 新しく䜜成された列は いけない ある NOT NULL。 ロヌルバックを実行するず、叀いアプリケヌションは新しい列を認識しないため、ロヌルバック䞭にそれをむンストヌルしたせん。 Insert. しかし、この制玄を远加するず、デヌタベヌスは次のようになりたす。 v2、これには新しい列の倀を蚭定する必芁がありたす。 それは制限違反に぀ながりたす。

それは重芁です。 メ゜ッドを削陀する必芁がありたす getLastName()、バヌゞョンでは 3.0.0 コヌドには列ずいう抂念がありたせん last_name。 これは、そこに null が蚭定されるこずを意味したす。 メ゜ッドを終了しお、次のチェックを远加できたす。 null、しかし、はるかに良い解決策は、ロゞックでそれを確認するこずです getSurname() れロ以倖の正しい倀を遞択したした。

A/B テスト

珟状ではアプリ版はありたす 1.0.0、本番環境にデプロむされ、デヌタベヌスは v1。 バヌゞョン アプリケヌションの XNUMX 番目のむンスタンスをデプロむする必芁がありたす 2.0.0これによりデヌタベヌスが曎新されたす v2.

手順

  1. バヌゞョンアプリケヌションの新しいむンスタンスがデプロむされたす 2.0.0これによりデヌタベヌスが曎新されたす v2
  2. その間、䞀郚のリク゚ストはバヌゞョン むンスタンスによっお凊理されたした 1.0.0
  3. 曎新は成功し、バヌゞョン アプリケヌションのむンスタンスが耇数実行されおいたす。 1.0.0 および他のバヌゞョン 2.0.0. 誰もがデヌタベヌスず通信したす。 v2
  4. バヌゞョン 1.0.0 デヌタベヌス内の姓の列は䜿甚したせんが、バヌゞョンを䜿甚したす。 2.0.0 を䜿甚したす。 これらは盞互に干枉しないため、゚ラヌは発生しないはずです。
  5. バヌゞョン 2.0.0 デヌタを叀い列ず新しい列の䞡方に保存し、䞋䜍互換性を確保したす

それは重芁です。 新旧の列の倀に基づいお項目をカりントするク゚リがある堎合は、重耇した倀が存圚するこずを芚えおおく必芁がありたす (おそらくただ移行䞭です)。 たずえば、姓 (列の名前は䜕でも) が文字で始たるナヌザヌの数を数えたい堎合、 A、その埌、デヌタ移行が完了するたで (old → new 列)、新しい列をク゚リするず、デヌタが矛盟する可胜性がありたす。

アプリケヌションのロヌルバック

アプリ版ができたした 2.0.0 ずデヌタベヌス v2.

手順

  1. アプリケヌションをそのバヌゞョンにロヌルバックする 1.0.0.
  2. バヌゞョン 1.0.0 デヌタベヌス内の列を䜿甚したせん surname、したがっおロヌルバックは成功するはずです

DBの倉曎

デヌタベヌスには、ずいう名前の列が含たれおいたす。 last_name.

Flyway ゜ヌス スクリプト:

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

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

スクリプトの远加 surname.

泚意しおください。 远加する列には NOT NULL 制玄を远加できないこずに泚意しおください。 JAR をロヌルバックするず、叀いバヌゞョンでは远加された列が認識されず、自動的に NULL に蚭定されたす。 このような制限がある堎合、叀いアプリケヌションは単玔に壊れおしたいたす。

-- NOTE: This field can't have the NOT NULL constraint cause if you rollback, the old version won't know about this field
-- and will always set it to NULL
ALTER TABLE PERSON ADD surname varchar(255);

-- WE'RE ASSUMING THAT IT'S A FAST MIGRATION - OTHERWISE WE WOULD HAVE TO MIGRATE IN BATCHES
UPDATE PERSON SET PERSON.surname = PERSON.last_name

コヌドの倉曎

デヌタを次のように保存したす last_name、および surname。 同時に私たちはから読みたす last_nameこの列が最も関連性があるためです。 デプロむメントプロセス䞭に、䞀郚のリク゚ストはただ曎新されおいないアプリケヌションむンスタンスによっお凊理される可胜性がありたす。

/*
 * Copyright 2012-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package sample.flyway;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;
    private String surname;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * Reading from the new column if it's set. If not the from the old one.
     *
     * When migrating from version 1.0.0 -> 2.0.0 this can lead to a possibility that some data in
     * the surname column is not up to date (during the migration process lastName could have been updated).
     * In this case one can run yet another migration script after all applications have been deployed in the
     * new version to ensure that the surname field is updated.
     *
     * However it makes sense since when looking at the migration from 2.0.0 -> 3.0.0. In 3.0.0 we no longer
     * have a notion of lastName at all - so we don't update that column. If we rollback from 3.0.0 -> 2.0.0 if we
     * would be reading from lastName, then we would have very old data (since not a single datum was inserted
     * to lastName in version 3.0.0).
     */
    public String getSurname() {
        return this.surname != null ? this.surname : this.lastName;
    }

    /**
     * Storing both FIRST_NAME and SURNAME entries
     */
    public void setSurname(String surname) {
        this.lastName = surname;
        this.surname = surname;
    }

    @Override
    public String toString() {
        return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName + ", surname=" + this.surname
                + "]";
    }
}

ステップ 3: コヌドから last_name を削陀する

アプリケヌションのバヌゞョン 3.0.0

DBバヌゞョン:v3

コメント

泚蚘per.: どうやら、元の蚘事では、䜜成者がステップ 2 からこのブロックのテキストを誀っおコピヌしたようです。このステップでは、列を䜿甚する機胜を削陀するこずを目的ずしたアプリケヌション コヌドの倉曎を行う必芁がありたす。 last_name.

新しい列を远加しおその内容をコピヌするこずで、䞋䜍互換性のあるデヌタベヌスの倉曎を䜜成したした。 たた、JAR をロヌルバックしたり、叀い JAR を実行したりしおも、実行䞭に壊れるこずはありたせん。

アプリケヌションのロヌルバック

珟圚はアプリ版がありたす 3.0.0 ずデヌタベヌス v3..。 バヌゞョン 3.0.0 デヌタを保存したせん last_name。 これは、次のこずを意味したす surname 最新の情報が保存されおいたす。

手順

  1. アプリケヌションをそのバヌゞョンにロヌルバックする 2.0.0.
  2. バヌゞョン 2.0.0 甚途ず last_name О surname.
  3. バヌゞョン 2.0.0 かかりたす surname、れロでない堎合、それ以倖の堎合 -last_name

デヌタベヌスの倉曎

デヌタベヌスの構造に倉曎はありたせん。 次のスクリプトは、叀いデヌタの最終移行を実行するために実行されたす。

-- WE'RE ASSUMING THAT IT'S A FAST MIGRATION - OTHERWISE WE WOULD HAVE TO MIGRATE IN BATCHES
-- ALSO WE'RE NOT CHECKING IF WE'RE NOT OVERRIDING EXISTING ENTRIES. WE WOULD HAVE TO COMPARE
-- ENTRY VERSIONS TO ENSURE THAT IF THERE IS ALREADY AN ENTRY WITH A HIGHER VERSION NUMBER
-- WE WILL NOT OVERRIDE IT.
UPDATE PERSON SET PERSON.surname = PERSON.last_name;

-- DROPPING THE NOT NULL CONSTRAINT; OTHERWISE YOU WILL TRY TO INSERT NULL VALUE OF THE LAST_NAME
-- WITH A NOT_NULL CONSTRAINT.
ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;

コヌドの倉曎

泚蚘per.: このブロックの説明も、䜜成者がステップ 2 から誀っおコピヌしたものです。蚘事の論理に埓っお、このステップでのコヌドの倉曎は、その列で機胜する芁玠をブロックから削陀するこずを目的ずしおいる必芁がありたす。 last_name.

デヌタを次のように保存したす last_name、および surname. さらに、コラムから読むず、 last_name、それが最も関連性があるためです。 デプロむメントプロセス䞭、䞀郚のリク゚ストはただアップグレヌドされおいないむンスタンスによっお凊理される堎合がありたす。

/*
 * Copyright 2012-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package sample.flyway;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String surname;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSurname() {
        return this.surname;
    }

    public void setSurname(String lastname) {
        this.surname = lastname;
    }

    @Override
    public String toString() {
        return "Person [firstName=" + this.firstName + ", surname=" + this.surname
                + "]";
    }
}

ステップ 4: デヌタベヌスから last_name を削陀する

アプリケヌションのバヌゞョン 4.0.0

DBバヌゞョン: v4

コメント

バヌゞョンコヌドがあるため、 3.0.0 コラムは䜿わなかった last_nameにロヌルバックしおも、実行䞭に悪いこずは䜕も起こりたせん。 3.0.0 デヌタベヌスから列を削陀した埌。

スクリプト実行ログ

We will do it in the following way:

01) Run 1.0.0
02) Wait for the app (1.0.0) to boot
03) Generate a person by calling POST localhost:9991/person to version 1.0.0
04) Run 2.0.0
05) Wait for the app (2.0.0) to boot
06) Generate a person by calling POST localhost:9991/person to version 1.0.0
07) Generate a person by calling POST localhost:9992/person to version 2.0.0
08) Kill app (1.0.0)
09) Run 3.0.0
10) Wait for the app (3.0.0) to boot
11) Generate a person by calling POST localhost:9992/person to version 2.0.0
12) Generate a person by calling POST localhost:9993/person to version 3.0.0
13) Kill app (3.0.0)
14) Run 4.0.0
15) Wait for the app (4.0.0) to boot
16) Generate a person by calling POST localhost:9993/person to version 3.0.0
17) Generate a person by calling POST localhost:9994/person to version 4.0.0

Starting app in version 1.0.0
Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}

Starting app in version 2.0.0

Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}

Generate a person in version 2.0.0
Sending a post to 127.0.0.1:9992/person. This is the response:

{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}

Killing app 1.0.0

Starting app in version 3.0.0

Generate a person in version 2.0.0
Sending a post to 127.0.0.1:9992/person. This is the response:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}

Generate a person in version 3.0.0
Sending a post to 127.0.0.1:9993/person. This is the response:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}

Killing app 2.0.0

Starting app in version 4.0.0

Generate a person in version 3.0.0
Sending a post to 127.0.0.1:9993/person. This is the response:

{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}

Generate a person in version 4.0.0
Sending a post to 127.0.0.1:9994/person. This is the response:

{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}

DBの倉曎

盞察的に v3 列を削陀するだけです last_name 䞍足しおいる制限を远加したす。

-- REMOVE THE COLUMN
ALTER TABLE PERSON DROP last_name;

-- ADD CONSTRAINTS
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;

コヌドの倉曎

コヌドに倉曎はありたせん。

出力

䞋䜍互換性のあるデプロむメントをいく぀か実行するこずで、䞋䜍互換性のない列名の倉曎を適甚するこずに成功したした。 実行されたアクションの抂芁は次のずおりです。

  1. アプリケヌションバヌゞョンのデプロむメント 1.0.0 с v1 デヌタベヌス スキヌマ (列名 = last_name)
  2. アプリケヌションバヌゞョンのデプロむメント 2.0.0, デヌタを保存するもの last_name О surname。 アプリケヌションは以䞋から読み取りたす last_name。 デヌタベヌスのバヌゞョンは次のずおりです v2のような列を含む last_nameず surname. surname l のコピヌですast_name。 (泚: この列には非 null 制玄を蚭定するこずはできたせん)
  3. アプリケヌションバヌゞョンのデプロむメント 3.0.0にデヌタのみを保存したす。 surname そしお姓から読みたす。 デヌタベヌスに関しおは、最埌の移行が行われおいたす last_name в surname。 制限もある NOT NULL から削陀されたした last_name。 デヌタベヌスの珟圚のバヌゞョンは次のずおりです v3
  4. アプリケヌションバヌゞョンのデプロむメント 4.0.0 - コヌドには倉曎が加えられたせん。 デヌタベヌスの展開 v4を削陀したす。 last_name。 ここで、䞍足しおいる制玄をデヌタベヌスに远加できたす。

このアプロヌチに埓うこずで、デヌタベヌスずアプリケヌションの互換性を損なうこずなく、い぀でも XNUMX ぀のバヌゞョンをロヌルバックできたす。

コヌド

この蚘事で䜿甚されおいるすべおのコヌドは、次の堎所から入手できたす。 githubの。 以䞋、補足説明です。

プロゞェクト

リポゞトリのクロヌンを䜜成するず、次のフォルダヌ構造が衚瀺されたす。

├── boot-flyway-v1              - 1.0.0 version of the app with v1 of the schema
├── boot-flyway-v2              - 2.0.0 version of the app with v2 of the schema (backward-compatible - app can be rolled back)
├── boot-flyway-v2-bad          - 2.0.0.BAD version of the app with v2bad of the schema (backward-incompatible - app cannot be rolled back)
├── boot-flyway-v3              - 3.0.0 version of the app with v3 of the schema (app can be rolled back)
└── boot-flyway-v4              - 4.0.0 version of the app with v4 of the schema (app can be rolled back)

スクリプト

以䞋のスクリプトで説明されおいるスクリプトを実行するず、デヌタベヌスに察する䞋䜍互換性のある倉曎ず互換性のない倉曎が瀺されたす。

芋る 䞋䜍互換性のある倉曎を䌎うケヌス、 走る

./scripts/scenario_backward_compatible.sh

そしお芋るために 䞋䜍互換性のない倉曎が加えられた堎合、 走る

./scripts/scenario_backward_incompatible.sh

Spring Boot サンプル フラむりェむ

すべおの䟋はから匕甚されおいたす Spring Boot Sample Flyway.

芋おみるこずができたす http://localhost:8080/flyway、スクリプトのリストがありたす。

この䟋には、H2 コン゜ヌル ( http://localhost:8080/h2-console) デヌタベヌスのステヌタスを衚瀺できるようになりたす (デフォルトの jdbc URL は jdbc:h2:mem:testdb).

さらに

私たちのブログの他の蚘事もお読みください。

出所 habr.com

コメントを远加したす