rastam on rails

東京在住のマレーシア人 Rubyist

バックフィル 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 #577: 日本語サマリー

rubyweekly.com

Highlights

Doing a Rails PR Code Review with a Rails Core Team Member

Rails コアチームのペアレビュー実況。主にベストなメソッドインタフェースを議論してる。

Articles & Tutorials

Making a GitHub Issue-Style File Uploader Using Stimulus and Active Storage

GitHub のコピペ or D&D だけでファイルアップロード・添付する、超便利機能を Stimulus で実装する方法。

Ruby Weekly #567: 日本語サマリー

rubyweekly.com

Highlights

Privacy-Aware Rails Consoles with console1984 and audits1984

rails console でユーザの個人情報など匿名化してくれる console1984 gem と、console1984 利用ログをブラウザーで照会できるようにしてくれる audits1984 gem。

Articles & Tutorials

Responsible Monkeypatching in Ruby

bundle update に強いモンキーパッチの書き方。ざっくりいうと、上書きしている API が変わっていないことをモンキーパッチ内で確認すること。変わった場合は例外を投げること。

Submitting Many ActionMail Jobs at Once with Sidekiq

大量のメールを deliver_later しようとしたら、Redis のキューにジョブを積むだけでも長時間がかかり、リクエストがその間ずっと待っている状態。

Sidekiq::Client.push_bulk 使えば一瞬で終わるが、ActionMailer は対応してないから、オレオレ実装せざるを得ない。

Code & Tools

wipe_out: Library for Resetting Data in Rails ActiveRecord Models

ユーザ退会などで個人情報をマスキング・匿名化してくれる gem。

Ruby Weekly #560: 日本語サマリー

rubyweekly.com

Highlights

Ruby 3.0.2 Released (plus 2.7.4 and 2.6.8)

Security Fix あり。バージョンアップしましょう。

Understanding Factory Bot Syntax by Coding Your Own Factory Bot

オレオレ FactoryBot を実装してみることで FactoryBot の仕組みを理解する。deepL で翻訳できる記事もあれば、おまけに動画もある。

動画は TDD で実装してるスクリーンキャスト。TDD に興味がある人や、英語リスニングに自信がある人はぜひ。

Articles & Tutorials

The Shortest Path to Get Set Up with Automated Accessibility Testing in Rails

RSpec + axe gem で a11y テストを自動化した実装例アプリ。

Under The Hood of the includes Method in Rails

includes vs preload vs eager_load

Code & Tools

Fisk: A Pure Ruby x86-64 Assembler

ActiveRecord のメンテナーの tenderlove 先生が Ruby で実装したアセンブラー。

Heroku Database Connection Calculator

Heroku の必要な DB コネクション数を計算してくれるツール。

Ruby Weekly #552: 日本語サマリー

rubyweekly.com

Highlights

The History of RSpec

RSpec 作成者が gem の経歴を振り返った。

  • 2001 年に TDD を教えてた仕事がそもそものきっかけ。
  • XUnit 表記は分かりづらかった(actual・expected 引数の順番が直感的じゃなかった)から、授業で使える DSL 作った。
  • Bob Martin 先生の名言「Testing is about specification not verification」をもとに RSpec と名付けた。
  • 実務では Test::Unit 使うべきだと信じてたから gem 化しなかった。
  • RubyConf 2005 にやっと gem としてリリースしてみた。意外とバズった。
  • 2006 年に David Chelimsky 先生がコントリビューターとして参戦。

Articles & Tutorials

The Five Rules of Simple RSpec Tests

RSpec のベストプラクティス集。

Some Useful Active Support Features You May Not Have Heard Of

知られざる ActiveSupport 便利機能。

  • Callbacks
  • Configurable
  • CurrentAttributes
  • MessageVerifier
  • MessageEncryptor

Start With High-Level Tests

TDD の際は高レベルテストから書け、というアドバイス

Three Ways To Avoid Duplicate Sidekiq Jobs

多数 Sidekiq ジョブの同時実行防止方法。

  • フラグで制御
  • キューにジョブが積まれてるか確認。
  • Sidekiq Enterprise に課金して sidekiq_options(unique_for:) 設定
  • sidekiq-unique-jobs gem(一部 Lua 言語で実装されてる)

ActiveJob の場合は

Top Ten git Tips and Tricks

役に立つ git 裏技。

  • 空コミット
  • ログを読みやすくする
  • 不要なローカルブランチを捨てる
  • git rebase などでうっかり消したコミット復元
  • 部分的 git add
  • git bisect 自動化

Ruby Weekly #550: 日本語サマリー

rubyweekly.com

Highlights

(Some of) What's Cooking in Rails 7

Rails 7 実装中機能。気になったのは:

  • 画像が全部デフォで lazy ロードするように設定できる
  • has_one :through アソシエーションも buildcreate できる
  • ActiveRecord カラム暗号化

Building a Component Library in Rails with Storybook

View Component を Storybook でプレビューする話。view_component_storybook gem で。

Basecamp's Employee Speech Policy Controversy

Basecamp 社内騒動。あれ以来は社員 1/3 退職しちゃった。

A 'trick' for Speeding up bundle install

MAKE="make --jobs $(sysctl -n hw.ncpu)" 環境変数を付けると、bundle install 並列実行で高速化できるらしい。