アルパカログ

まじめなことを書きます。

ERB の partial が重かった話

業務で開発・保守している社内向け Web アプリケーションの画面の一つが重くて開けなくなりました。
エラーログからはタイムアウトにより unicorn プロセスが kill されていることがわかりました。
該当画面では3000レコードを DB から取得して一覧表示しようとしていましたが N+1 問題は解決済みで Slow Query は出ていません。
プロファイリングの結果 ERB テンプレートで使用している partial が重いということが判明しました。

サンプルコード

サンプルには Rack アプリケーションとして Sinatra を、プロファイリングには Rack::Lineprof を使用しました。

app.rb

require "sinatra"
require "rack-lineprof"

use Rack::Lineprof

get "/" do
  erb :index
end

views/index.erb

<% 2000.times do %>
  foo
  <%=erb :partial, layout: false %>
<% end %>

views/partial.erb

bar

プロファイリング結果

[Rack::Lineprof] ===============================================================

views/index.erb
 229.8ms     1 |   1  <% 2000.times do %>
   3.2ms  2000 |   2    foo
 192.8ms 10000 |   3    <%=erb :partial, layout: false %>
               |   4  <% end %>

partial で時間がかかっているのがわかります。
今回のケースですとループの中で複数の partial を使用しており、それぞれ数秒程度かかっていました。
解決方法としては、場合分けや計算等が目的であれば helper メソッドを使う、日時フォーマットの変換が目的であれば I18n を使うことが挙げられます。