バックフィル SQL の確認事項
INSERT INTO new_table (a, b) SELECT old_table.a, old_table.b FROM old_table INNER JOIN parent_table ON old_table.parent_table_id = parent_table_id LEFT JOIN new_table ON parent_table.id = new_table.parent_table_id WHERE new_table.parent_table_id IS NULL
のような SQL でバックフィルする際、どんなチェックをすれば安心して本番 DB で実行できるか、メモっておきました。
トランザクション張ってないか?
Rails の migration で実行する場合は、INSERT 先テーブルにロックがかかっちゃうから、strong migrations に従って disable_ddl_transaction!
でトランザクション外に実行すること。
実行計画はどんな感じか?
SELECT 文だけ切り取って頭に EXPLAIN
付けて実行してみること。
SELECT old_table.a, old_table.b FROM old_table INNER JOIN parent_table ON old_table.parent_table_id = parent_table_id LEFT JOIN new_table ON parent_table.id = new_table.parent_table_id WHERE new_table.parent_table_id IS NULL
返ってくる実行計画は例えばこういう感じで、
Aggregate (cost=23.93..23.93 rows=1 width=4) -> Index Scan using fi on foo (cost=0.00..23.92 rows=6 width=4) Index Cond: (i < 10)
最初の cost=23.93..23.93
の上限 23.93
が Total Cost で、チューニングは基本この Total Cost を減らしていきたい。試してみるチューニング案としては例えば
- 条件を
JOIN ... ON
からWHERE
に移すとか - SUBSELECT を減らすとか
Seq Scan
が一番重い処理で、減らせるなら減らす
INSERT 先テーブル 🆚 INSERT 元テーブル 🆚 SELECT 文のレコード数が一致してるか?
※ ユーザ操作で INSERT 先テーブル
と INSERT 元テーブル
両方にも書き込んでる現行システムが前提。
SELECT 文のレコード数
+ INSERT 先テーブルのレコード数
= INSERT 元テーブルのレコード数
つまり
移行予定のレコード数
+ 移行済みのレコード数
= 総レコード数学
SELECT COUNT(*) '移行予定のレコード数' FROM old_table INNER JOIN parent_table ON old_table.parent_table_id = parent_table_id LEFT JOIN new_table ON parent_table.id = new_table.parent_table_id WHERE new_table.parent_table_id IS NULL UNION SELECT COUNT(id) '移行済みのレコード数' FROM new_table UNION SELECT COUNT(id) '総レコード数' FROM old_table
SQL 文にはよるが、レコード数が一致してれば、JOIN がおかしくないと思っちゃっていいでしょう。
※ staging など全環境の DB に対して実行すること!
UNIQUE インデックスに引っ掛からないか?
INSERT 先テーブルに UNIQUE インデックスが張ってある場合は、データの中に重複するデータがないことを確認。
SELECT COUNT(*), COUNT(DISTINCT old_table.a), COUNT(DISTINCT (old_table.a, old_table.b)) FROM old_table INNER JOIN parent_table ON old_table.parent_table_id = parent_table_id LEFT JOIN new_table ON parent_table.id = new_table.parent_table_id WHERE new_table.parent_table_id IS NULL
COUNT が一致してれば、重複してないと思っちゃっていいでしょう。
NOT NULL 制約に引っ掛からないか?
INSERT 先テーブルに NOT NULL 制約がかかってる場合は、データの中にヌルがないことを確認。
SELECT COUNT(old_table.a IS NULL OR NULL) FROM old_table INNER JOIN parent_table ON old_table.parent_table_id = parent_table_id LEFT JOIN new_table ON parent_table.id = new_table.parent_table_id WHERE new_table.parent_table_id IS NULL
ゼロであれば、ヌルがないと思っちゃっていいでしょう。
FK に引っ掛からないか?
INSERT 先テーブルに FK が張ってある場合は、関連テーブルに該当レコードがちゃんと存在することを確認。
SELECT old_table.a, old_table.b FROM old_table INNER JOIN parent_table ON old_table.parent_table_id = parent_table_id LEFT JOIN new_table ON parent_table.id = new_table.parent_table_id WHERE new_table.parent_table_id IS NULL
INNER JOIN parent_table
してるのがそれ。
本番 DB のダンプに対して実行しても落ちないか?
とりあえず念のため
- エラーが発生しないか?
- 実行時間どのぐらいかかるか?
Ruby Weekly #613: 日本語サマリー
Articles Stories & Videos
Python 🆚 Ruby のシンタックス比較。Python はインスタンス変数を外から簡単に書き換えれて怖い。
Did You Know..
.. you can quickly 'test drive' over 100 programming-focused fonts on programmingfonts.org?
プログラミング用フォントがプレビューできるサイト。全角文字に明確に対応してるのは M PLUS Code と Binchotan Sharp。
Ruby Weekly #609: 日本語サマリー
Articles & Tutorials
Action Cable だと数千ソケットに耐えきれないらしい。AnyCable は同じ Rails + Action Cable を使いながら、ソケット周りを Go プロセスで処理してくれるから爆速。
foo[:bar] || baz
を foo.fetch(:bar, baz)
に書き換える前に要注意。後者だと baz
は常に実行されるから。
Code & Tools
Motion 0.7: Pure Ruby Reactive Frontend UI Components for Rails
ViewComponent に Action Cable 機能を簡単に追加してくれる gem。
Ruby Weekly #603: 日本語サマリー
Highlights
There's been a bit of chatter online over the past week about the future of Heroku that you might want to keep an eye on if you're a user.
Salesforce は Heroku をこれ以上メンテしない(まさかサービス終了?)かもという噂。Heroku の中の人の暴露記事と HN スレより。
- Salesforce は Periwinkle という新規プロダクト開発中。無料枠はなく、Heroku より機能が少ない。Periwinkle リリース後は Heroku サービス終了かも。
- Salesforce は Heroku にこれ以上投資しない。ここ数年は機能追加しなくなった。エンジニア採用もしてない、かつエンジニア大量流出。
- Heroku のソースがレガシー化してて、メンテできる人材が足りなくなった。先日の GitHub OAuth トークン漏洩への対応が遅かったのもこの人手不足が原因らしい。
Ruby Weekly #600: 日本語サマリー
Articles & Tutorials
#<==>
#succ
メソッド実装だけでオブジェクトが Range
化できる。
Code & Tools
command_mapper: A Way to Map External Commands to Ruby Classes
CLI コマンドのラッパークラスを生成してくれる gem。コマンドの --help
や man ページをパースしてオプションを全部生成という、結構すごいことやってくれるらしい。
Metabase の OSS 版的な gem を ankane 先生が作ってくれた。
Ruby Weekly #591: 日本語サマリー
Highlights
Introducing Propshaft: The Future of Asset Pipelining in Rails?
Rails の新しい asset pipeline になる Propshaft 紹介 by DHH。
今年の Ruby on Rails ユーザ動向調査。
Ruby 3.1 のエラー発生箇所をより分かりやすくしてくれる gem。
GitHub のマークダウンで図描けるようになった。
筆者が React から Rails に惚れた理由。フォルダー構造、ORM など決める手間が省けて、Rails の Convention over Configuration に全任せれできたから。
Articles & Tutorials
Rust で gem を書く手順。
スパイク時は TDD しない。
Code & Tools
gem 開発用 CLI。新規 gem プロジェクト生成や、rubygems へプッシュしてくれるコマンド完備。
Rambulance 2.2: Dynamically Render Error Pages or JSON Responses for Rails Apps
HTTP ステータス別エラー画面管理 gem。