はじめに
Java アプリケーションの MySQL のデータベースマイグレーションに Flyway を使っているのだが、 一部の ALTER TABLE 文は他の SQL の実行をブロックしてしまうので、pt-online-execute-chage を使いたい。
Flyway でデータベースマイグレーションを管理しつつ、pt-online-execute-chage
でマイグレーションを実行するようにした作業をメモする。
背景
Flyway は、データベースマイグレーションの実行や管理をしてくれる便利なツールなのだが、サービス稼働中に気軽に実行できないことがある。
例えば、 MySQL では ALTER TABLE などの DDL の種類によっては、他の DML 操作をブロックしてしまう。データ量が少なければ一瞬で終わるのだろうが、データ量が多いとスキーマ変更が終わるまで、数分、下手すると数時間ブロックしてしまう。
MySQL もオンライン DDL の範囲が増えており改善はされているが、パーティションを切っているテーブルではまだまだブロックしてしまう DDL の種類が多い。詳しくは こちら。
そこでオンライン・スキーマ・マイグレーションツールを使って、サービスのダウンタイムなしに DDL を実行したくなる。
ここでは pt-online-execute-chage を使った場合を説明する。
pt-online-execute-chage を使って実行した ALTER TABLE 文 は、Flyway のスキーマ変更管理テーブルには載らいないので、データベースマイグレーション管理ができていない状態になってしまう。
やりたいこと
- Flywayでデータベースマイグレーションを管理する
- 一部の ALTER TABLE 文 は pt-online-execute-chage を使って実行する
- pt-online-execute-chage を使って実行した ALTER TABLE 文も Flyway のスキーマ変更管理テーブルにも載せる
免責
このドキュメントは私の作業記録を差し支えない範囲で公開しているものです。
参考にされる場合は、自己責任でお願いします。特に以下に注意してください。
- pt-online-execute-chage の実行にはリスクが伴います。リスクについては リファレンス に記載されていますのでよく読んで把握された上で導入することをおすすめします。
- 記載したコード片は簡略化したり一般化したところがあります。そのまま動かすことは推奨しません。
- 検証環境でのテストや、失敗した場合のリカバリー計画など立てた上で実行することをおすすめします。
- 排他制御については記載していませんが、CI/CD ツールの仕組みで排他制御しています。
現状
対象のアプリケーションは Flyway Gradle plugin を入れていて、gradle コマンドから flyway によるマイグレーションを実行できる。
以下のようなコマンドを CI/CD ツールから実行している
./gradlew \
-Dflyway.url="jdbc:mysql://{DB_URL}/{DB_NAME}" \
-Dflyway.user={DB_USER} \
-Dflyway.password={DB_PASSOWRD} \
flywayMigrate
やったこと
以下のような処理を CI/CD ツールで実行することにした。
- マイグレーション可能な状態か確認
- 実行対象のマイグレーションバージョンを確認
- flyway_schema_history にレコードを INSERT
- ALTER TABLE 文の抜き出し
- pt-online-execute-chage を実行
- flyway_schema_history のレコードを success に更新
- flyway_schema_history のレコードの description, checksum を正しい値に更新
flyway_schema_history は Flyway スキーマ管理テーブル。
それぞれ簡単に説明する。
マイグレーション可能な状態か確認
次のような SQL で flyway_schema_history テーブルに実行中もしくは実行失敗したレコードがないか確認する。
SELECT * FROM flyway_schema_history WHERE success <> 1
実行対象のマイグレーションバージョンを確認
実行しようとしているマイグレーション対象のバージョンが、まだFlywayで実行されていないバージョンの中で一番小さいバージョンかどうか確認する。
target_version = "{マイグレーション対象のバージョン}"
flyway_info=$(./gradlew ... flywayInfo)
pending_version=`echo "$flyway_info" | grep Pending | awk -F'|' '{print $3}' | sed 's/ *$//g' | sed 's/^ *//g' | head -n1`
if [ "$pending_version" != "" ] && [ "$pending_version" = "$target_version" ]; then
# OK
else
# NG
exit 1
fi
flyway_schema_history にレコードを INSERT
flyway_schema_history に対象バージョンのレコードを INSERT する。
installed_rank の最大値を取得して
SELECT MAX(installed_rank) AS rank FROM flyway_schema_history;
flyway_schema_history に INSERT する。
INSERT INTO flyway_schema_history (
installed_rank,
version,
description,
type,
script,
checksum,
installed_by,
installed_on,
execution_time,
success
) VALUES (
${installed_rank},
${version},
'',
'SQL',
${migration_file_name},
null,
${db_user},
now(),
0,
false
);
pt-online-execute-chage を実行
FLyway用のマイグレーションファイルから、ALTER TABLE 文を読み込んでから、pt-online-schema-change コマンドを実行する。
pt-online-schema-change \
--execute \
--charset=utf8 \
--alter="$ALTER_TABLE_STATEMENT" \
--host=${DB_URL} \
--user=${DB_USER} \
--password=${DB_PASSWORD} \
--pid=${PID_FILE} \
--statistics \
--version-check \
--print
※ 省略したが dry-run を実行してから execute を実行している
flyway_schema_history のレコードを success に更新
flyway_schema_history に対象バージョンのレコードを success に更新する。
UPDATE flyway_schema
SET success = true
, execution_time = ${execution_time}
WHERE installed_rank = ${installed_rank}
AND version = ${version}
flyway_schema_history のレコードの description, checksum を正しい値に更新
flyway repair で description, checksum を正しい値に更新する。
./gradlew \
-Dflyway.url="jdbc:mysql://{DB_URL}/{DB_NAME}" \
-Dflyway.user={DB_USER} \
-Dflyway.password={DB_PASSOWRD} \
flywayRepair
まとめ
この記事では、Flyway 7 から導入された Script migrations は使っていない。これを使えばもっとスマートに連携出来る気がしている。
ただ強引なやり方ではあるが、Flyway と pt-online-schema-change を連携できるようになった。
連携できるようになって、サービスのダウンタイムが発生するような ALTER TABLE 文を、ダウンタイムなしに実行できるようになった。
深夜帯の作業だったものが営業時間内にも実行可能な作業になって、健全な職場環境づくりに貢献できたのではないだろか 💪