Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Railsのマイグレーション、どこまで安全にできるか
Search
Manami Nakamura
August 24, 2023
0
69
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
TROCCOで開発生産性を計測してみた
mnmandahalf
0
100
データカタログのアクセスコントロールを考える
mnmandahalf
0
41
primeNumberでのRBS導入の現在 && RBS::Traceでinline RBSを拡充してみた
mnmandahalf
0
560
COMETA®開発の裏側をご紹介
mnmandahalf
0
940
よく考えずにRDSを暗号化したら辛かった話
mnmandahalf
0
26
Featured
See All Featured
Optimising Largest Contentful Paint
csswizardry
37
3.5k
Embracing the Ebb and Flow
colly
88
4.9k
It's Worth the Effort
3n
187
29k
GitHub's CSS Performance
jonrohan
1032
470k
Balancing Empowerment & Direction
lara
5
800
Site-Speed That Sticks
csswizardry
13
1k
How to train your dragon (web standard)
notwaldorf
97
6.4k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.6k
Context Engineering - Making Every Token Count
addyosmani
9
530
How To Stay Up To Date on Web Technology
chriscoyier
791
250k
Building Flexible Design Systems
yeseniaperezcruz
330
39k
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