Webアプリケションのパフォーマンスに多く影響してしまう N+1 問題。
正直製品レベルのアプリケーションだと基本的にないのが当たり前だと思っていますが、うっかりミスで残っちゃったりするのも事実。
なので N+1 問題を教えてくれるgem、Bullet を使っている人は多いと思います。
最近では Rails では API サーバのみを作り、フロントエンドは JS フレームワークやスマホアプリで実装というケースが増えてきました。
こういったプロジェクトでは Rails で画面をまったく作らないので、画面があること前提の開発支援系 gem を使えなくて困ったりします。
そこで bullet に関してはテスト環境で動作させるようにしました。
bullet のテスト環境設定
とっても簡単です。
config.after_initialize do
Bullet.enable = true
Bullet.bullet_logger = true
Bullet.raise = false # raise an error if n+1 query occurs
end
if Bullet.enable?
config.before(:each) do
Bullet.start_request
end
config.after(:each) do
Bullet.perform_out_of_channel_notifications if Bullet.notification?
Bullet.end_request
end
end
これだけ。
bullet の警告の確認
bullet を通常の画面の存在する Web アプリで実行した場合は、画面表示時に Javascript の alert ダイアログが表示されるという、非常にウザい 分かりやすい通知方法になっていると思います。
API サーバでは画面がないのでそういった確認方法は使えないので、代わりにログを吐く設定をテスト環境で行っています。
bullet のログは log/bullet.log
に吐かれますので、テスト実行時に tail
するなりしておくと良いかもしれません。
後述していますが、N+1 問題警告時にテストが落ちるように設定することもできます。
解説のようなもの。
公式の bullet#Run-in-tests に書いてる内容ほぼそのままです。
ただしあちらの内容だと Bullet.raise = true
に設定されており、N+1 問題のあるクエリが実行されると軒並みテストが落ちるようになるので、若干鬼設定かなあと false
に変えています。
より確実に bullet の警告に気づくようにしたいのであれば、公式と同じくここを true
にしておくと良いです。
実際にはこの手のテストの設定は rails_helper.rb
に書いてる人も多いでしょうから、こんな風になることの方が多いかもしれません。
:
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
config.order = 'random'
config.include FactoryGirl::Syntax::Methods
:
config.before(:each) do
DatabaseCleaner.strategy = :transaction
FactoryGirl.reload
DatabaseCleaner.start
# ↓ココ
Bullet.start_request if Bullet.enable?
end
config.after(:each) do
DatabaseCleaner.clean
# ↓ココから
if Bullet.enable?
Bullet.perform_out_of_channel_notifications if Bullet.notification?
Bullet.end_request
end
# ↑ココまで
end
end
bullet 用に処理部分を追加する必要はないので、config.before(:each)
や config.after(:each)
の、DatabaseCleaner や DatabaseRewinder の呼び出しを行っているだろう当たりに適当に突っ込んでおけば動きます。
と、Rspec 前提で書いてますが他のテスティングフレームワークだとどうなるのか良く分かりません。
公式に書かれているように Bullet の開始と終了処理を、個々の example 実行の前後で呼び出してあげると動くと思います。