rake task で bullet を使って N+1 問題を検出
N+1 問題を検出する bullet は、rake タスクではそのままでは動作しないです。
各 rake タスクの最初と最後に Bullet を開始・終了する記述を追加する必要があります。こんな風に。
$ vi lib/tasks/hoge.rake namespace :hoge do task :hoge => :environment do |_task| # Bullet開始 Bullet.start_request # rakeタスク本体 〜N+1問題を添えて〜 Shop.limit(3).each do |shop| puts shop.items.map(&:name).to_s end # Bullet終了 Bullet.perform_out_of_channel_notifications if Bullet.notification? Bullet.end_request end end
こんなのいちいち全タスクに書きたくないですよね。
なので全タスクの開始時・終了時に呼ばれる hook を定義して、その中で Bullet を開始・終了させようと思います。
lib/tasks/hooks.rake
(ファイル名はなんでもいいです)に、以下のような2つのタスクを定義します。それぞれ、rake タスク開始時と終了時に呼ばれる hook です。
$ vi lib/tasks/hooks.rake desc 'rakeタスク開始時のhook' task before_hook: :environment do # Bullet開始 Bullet.start_request end desc 'rakeタスク終了時のhook' task after_hook: :environment do at_exit do # Bullet終了 Bullet.perform_out_of_channel_notifications if Bullet.notification? Bullet.end_request end end
これらの hook を他の全タスクの開始時・終了時に呼ぶように Rakefile
に追記します。
$ vi Rakefile require_relative "config/application" Rails.application.load_tasks # ↓ここから追記 # タスク実行前後の共通処理を追加 Rake.application.tasks.each do |task| next if %w[before_hook after_hook environment].include?(task.name) task.enhance([:before_hook, :after_hook]) end
最初に載せた N+1 な rake タスクからは Bullet 開始・終了の記述を削除できます。
$ vi lib/tasks/hoge.rake namespace :hoge do task :hoge => :environment do |_task| # rakeタスク本体 〜N+1問題を添えて〜 Shop.limit(3).each do |shop| puts shop.items.map(&:name).to_s end end end
この状態で実行してみましょう。
$ bundle exec rake hoge:hoge
↓のようにログが吐かれて、ちゃんと N+1 問題が検出できるようになりました。
$ tail -0f log/bullet.log 2023-03-30 07:18:41[WARN] user: root USE eager loading detected Shop => [:items] Add to your query: .includes([:items]) Call stack /app/src/lib/tasks/hoge.rake:5:in `map' /app/src/lib/tasks/hoge.rake:5:in `block (3 levels) in <main>' /app/src/lib/tasks/hoge.rake:5:in `block (2 levels) in <main>'
やったね。