Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Railsのマイグレーション、どこまで安全にできるか
Search
Manami Nakamura
August 24, 2023
0
10
Railsのマイグレーション、どこまで安全にできるか
Gotanda.rb#54 にてお話しした資料です。
https://gotanda-rb.connpass.com/event/291206/
Manami Nakamura
August 24, 2023
Tweet
Share
More Decks by Manami Nakamura
See All by Manami Nakamura
COMETA®開発の裏側をご紹介
mnmandahalf
0
310
よく考えずにRDSを暗号化したら辛かった話
mnmandahalf
0
13
Featured
See All Featured
Facilitating Awesome Meetings
lara
50
6.1k
The Language of Interfaces
destraynor
154
24k
Practical Orchestrator
shlominoach
186
10k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
How To Stay Up To Date on Web Technology
chriscoyier
789
250k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
28
2.1k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
127
18k
VelocityConf: Rendering Performance Case Studies
addyosmani
326
24k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
Optimizing for Happiness
mojombo
376
70k
Bash Introduction
62gerente
609
210k
Transcript
Railsのマイグレーション、どこ まで安全にできるか Gotanda.rb#54
自己紹介 中村 愛美 (@mnmandahalf) 経歴 SE(2年) 株式会社リブセンス(5年半) 株式会社primeNumber(3ヶ月)← 現職 お仕事
ソフトウェアエンジニアとして、データ分析基盤の総合支援サービス trocco® を作っています 趣味 旅行✈、珈琲☕、ベルーガ🐬 コアラ🐨 マーモット🐭の動画を見る 今日はRails × MySQLの話をします (PostgreSQLユーザーのみなさん、雰囲気を感じ取っていただけると嬉しいです)
突然ですが、DDLの適用緊張しますよね?
DBマイグレーションの運用どうしてますか? • メンテを入れている • pt-online-schema-changeなどの利用で、テーブルコピーをしている • MySQLのオンラインDDLガンガン流してます!
どんなマイグレーション、スキーマ管理ツール使ってますか? • Railsの標準マイグレーション • Ridgepole • pt-online-schema-change • LHM •
その他のツール
今日は • Railsの標準マイグレーションで、ユーザー影響(小)でDDLを適用する • フェイルセーフ(?)な運用
MySQLのオンラインDDL • https://dev.mysql.com/doc/refman/8.0/ja/innodb-online-ddl-operations.html
オフラインDDLを炙り出す方法 • SQLの末尾に LOCK=NONE を付与して実行する • 適用できない場合はMySQLがエラーにしてくれる。テーブルをロックすることはな い • execute(“ALTER
TABLE …, LOCK=NONE;”) .
executeでSQL実行するよりマイグレーションヘルパー使いたい! • add_column や add_index 等のヘルパーは、 change メソッドがロールバックで きる、インデックスの一貫した命名などのメリットを享受できる •
マイグレーションヘルパーにオプションないかな? → ない
オンラインDDLでも、ALGORITHM=INSTANTとINPLACEがあるよね? • INSTANTとは → メタデータの変更しかしないので、DDLの適用が一瞬 • MySQL 8.0.12から導入された • INPLACEはINSTANTよりは時間がかかるので、以下のようなリスクがある
◦ レプリケーション遅延( Auroraの場合は少ないらしい) ◦ メモリの逼迫 ◦ 例えば数分かかるインデックス追加 DDLが後述のメタデータロック待ちのタイムアウトで失敗する可 能性がある。リリースフローにマイグレーションを含めている場合、なかなかマイグレーションが終 わらずリリースに影響が出ることも。 ◦ 例:セカンダリインデックスの追加・削除、位置指定してのカラム追加(〜 8.0.28) • 少ないですがALGORITHM=COPYで読み書き同時DML許可もあるようです
INSTANT DDL以外は怖いので禁止したい! • ALGORITHM=INSTANT を強制してみる • どんな時にワークする? ◦ とりあえずエラーにして、通常のマイグレーションフローから分離する ◦
カラムの追加は許可するが、位置の指定は禁止したい • (余談)インデックスの追加限定で、 add_index ヘルパーに index_algorithm オプションを発見 ◦ https://github.com/rails/rails/commit/e199dc1a570d4f0d9a07628268835bce5aab2732 ◦ 元々PostgreSQLのインデックス追加の際にテーブルロックを不要にするアルゴリズムを指定可能にしたときに、 MySQLのぶんもついでに追加 されたようだった
実装編 • 最終的に実行されるSQLの末尾にオプションを付与したい • MySQLの場合、 ActiveRecord::ConnectionAdapters::MySQL::DatabaseStatement#executeの引数に 最終的なSQLが渡される • ActiveRecord::ConnectionAdapters::Mysql2Adapterで、executeメソッドをオーバーライ ドしたモジュールをprependする
• ただし、マイグレーション時のSQLだけ上書きしたい • 実はもうありました:https://github.com/anthonyalberto/mysql_online_migrations • ちなみに、Ridgepoleには --alter-extra オプションがある https://github.com/ridgepole/ridgepole#add-extra-statement-to-alter
より簡単な別のやり方も考えてみた • https://gist.github.com/mnmandahalf/e532c30e56d679d6e805405538077e5c • Rake::Task["db:migrate"].enhance でマイグレーションタスクを拡張
DEMO
オンラインDDLでもメタデータロックの問題がある • 他のセッションからの変更でオブジェクトの整合性が取れなくならないようにする仕 組み • 例えば ◦ セッションAがtable_1に長時間SELECT(読み取り専用メタデータロック) ◦ セッションBがtable_1にカラム追加(排他メタデータロック)
◦ セッションCがtable_1にSELECT(読み取り専用メタデータロック) ◦ セッションBとCはセッションAの終了を待ち続ける ◦ 読み取り × 読み取りは競合しないが、読み取り × 排他は競合する
メタデータロック待ちリスクを回避する • マイグレーションのセッションの lock_wait_timeout システム変数を短く設定する • lock_wait_timeout=5 なら、セッションAの終了を5秒待つ • セッションAが終了しない場合マイグレーションセッションがタイムアウト
• ちなみに:Auroraのレプリカでは先に開始しているセッションが ABORTするそうです ◦ https://mita2db.hateblo.jp/entry/2020/07/20/153139
Railsでメタデータロック待ちリスクを回避する • execute(“SET SESSION lock_wait_timeout = 5;”) のようにすればいいが... • お悩みポイント
◦ マイグレーションファイルの changeに書くとロールバックできない ◦ 毎回指定するの忘れそう ◦ マイグレーション失敗の確率が高まるし、ちょっと時間おいて自動リトライできないかな?
strong_migrations gemならできる!💪 • https://github.com/ankane/strong_migrations • 危険なマイグレーションのチェックができる(メイン機能) ◦ 主にPostgreSQLのチェックや普遍的に危険な操作(カラム削除や型変更など)のチェックが充実し ており、先述のMySQLのオンラインDDLのチェックはあまりサポートしていないかも •
lock_wait_timeout を一括で変更できる ◦ 先ほどの例と同様に、 MySQLのアダプタを拡張している • 間隔をあけてのリトライもできる(実験的機能)
DEMO
結論 • オフラインDDLの実行時チェック • 非 INSTANT DDLの実行時チェック • メタデータロック待ち対策 •
メタデータロック獲得タイムアウトでDDLが失敗した場合のリカバリ ここまではRails標準マイグレーションのフローでもできそう! という実験でした
ご清聴ありがとうございました! 皆さんのチームで工夫していることがあればぜひ教えてください! FYI: strong_migrations gemの導入でRailsマイグレーションのメタデータロック待ちリスクに対処する https://zenn.dev/primenumber/articles/7af6e95ddbb91d