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
12
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
390
よく考えずにRDSを暗号化したら辛かった話
mnmandahalf
0
14
Featured
See All Featured
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
30
2.1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
What's in a price? How to price your products and services
michaelherold
244
12k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.2k
Optimizing for Happiness
mojombo
376
70k
Embracing the Ebb and Flow
colly
84
4.5k
Documentation Writing (for coders)
carmenintech
67
4.6k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
192
16k
Designing for Performance
lara
604
68k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
28
2.2k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3.1k
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