レビューしやすい PR:私のコミット戦略
面倒で時間がかかりがちな、プルリクのレビュー。一秒でも早く終わらせたいあまりに、コミットをなるべく小さい単位で積むようにしている。
Spike
まずは spike 書いて、要件の理解を深めながら、クラス構造を築いていく。この段階ではまだコミットしない。アイデアを膨らませ、実装方針のヒントを集めるだけ。
生成したコード
CLI コマンドなどでコードを生成した場合は、生成したままのコードを 1 つのコミットに積む。コミットメッセージには CLI コマンドを記載。こうすることで、レビュワーはコードの生成方法を確認でき、レビューの過程でこういったコミットをスキップすることができる。
生成したコードのカスタマイズ
生成したコードをコミットした後、そのコードのカスタマイズは別のコミットに載せること。自動生成したものと手動で修正したものの区別が明確になり、レビュワーが後者に集中できる。
新規クラス
新しく実装したクラスが最後まで残る確信を得たら、単体テストと一緒にコミットする。こうやってクラス単位でコミットすることで、後からコミットを並べ替えたり削除したりする柔軟性が生まれる。
リファクター
リファクターは、個別のコミットに。コミットメッセージは Martin Fowler のカタログ上のリファクター名にすれば良し。これにより、レビュワーは挙動が変わっていないことの確認に集中できる。
英語原文
バックフィル 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 先生が作ってくれた。