ActiveSupport#present? の危険性
何が起こったか
Railsのバージョンアップの作業をしていて、画面の動作確認を行っていたところ、一部のページで100秒を超えるようなレイテンシが発生していました。
DatadogのLATENCYではなかなか見ないm(分)という単位
調べてみると、ActiveSupport で定義されている present?
メソッドを使って、画像の有無の確認をしている(無い場合はデフォルト画像を出すような)部分で、問題が発生していました。
原因
プロダクトで使っているRailsをアップデートする作業の中で carrierwave
という gem のバージョンを上げていました。
CarrierWave は、Ruby on Rails でファイルアップロードを簡単に扱うための gem です。
そのアップデート内容に 3.1.0
で Add CarrierWave::Storage::Fog::File#empty?
の実装が含まれていました。
以下が empty?
が入ったCommitです。
present?
で問題が発生するのか
なぜ 以下のように ActiveSupport の present?
メソッドは blank?
メソッドの否定を返します。
そして blank?
メソッドは empty?
メソッドがあればそれを使う実装になっています。
今までは empty?
メソッドが無かったため blank?
メソッドでファイルの存在確認だけして false
が返り、その否定なので present?
メソッドは true
になっていました。
しかし、今回の変更により empty?
メソッドが新たに実装されたことで present?
メソッドを呼ぶとその処理が実行されるようになったのです。
結果、empty?
メソッドの実装がアップロードされているファイルのサイズが0かどうかまで確認するものだったために、クラウド上のストレージまでHEADでアクセスすることになりました。
そして数十枚の画像を表示する画面で、一枚一枚の画像でクラウド上へHEADのアクセスが走ることになり、大きなレイテンシが発生してしまった、というわけです。
現在 gem での対応が、以下のIssueで検討中のようです。
問題への対応
present?
メソッドを使わないようにしました。
具体的には url
メソッドはファイルが無い場合には nil
を返すので、その結果を nil?
を使って判定するようにしました。
- image_object.present?
+ image_object.url.nil?
まとめ
Railsアプリのバージョンアップ作業で、何かのObjectに empty?
が新しく実装されたら、ちょっと気を付けましょう!
present?
や blank?
は便利で色んなところで使いがちですが、こういう問題が発生した際は nil?
などで回避できるかも知れませんので、試してみてください。
Discussion