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
24
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
primeNumberでのRBS導入の現在 && RBS::Traceでinline RBSを拡充してみた
mnmandahalf
0
200
COMETA®開発の裏側をご紹介
mnmandahalf
0
680
よく考えずにRDSを暗号化したら辛かった話
mnmandahalf
0
19
Featured
See All Featured
Stop Working from a Prison Cell
hatefulcrawdad
269
20k
The Language of Interfaces
destraynor
158
25k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.3k
The Invisible Side of Design
smashingmag
299
50k
Embracing the Ebb and Flow
colly
85
4.7k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.6k
Designing Experiences People Love
moore
142
24k
Unsuck your backbone
ammeep
671
58k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
129
19k
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3.2k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
29
9.5k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
105
19k
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