Rails のキャッシュが memcached に保存されない(解決)

Rails + memcached (dalli)の組み合わせで、キャッシュがうまく動かないトラブルが発生したのでメモ。ちなみに環境はこんな感じです。

  • Ruby on Rails 3.2.14
  • Ruby 2.0.0-p247
  • memcached 1.4.14

結論

さきに結論から言うと、「memcached の最大キャッシュオブジェクトサイズを超えるサイズのデータをキャッシュしようとしていた」ことがトラブルの原因でした。

対策としては、dalli の設定を変更して、memcached にデータを圧縮して保存するようにしました。その結果、無事にキャッシュが動作するようになりました。

では、詳しく見て行きましょう。

発生したトラブル

普段開発しているRailsアプリケーションでは、レスポンス速度改善のために、memcached を使って、Action Caching の仕組みを導入しています。

Action Cachingは、Controller に以下のように記述することで、特定の Action のレンダリング結果をキャッシュできる仕組みです。

上のように書くことで、

  • Action呼出(初回): Action 内部の処理が実行されレスポンスのデータが生成される。データはキャッシュとして保存される。
  • Action呼出(2回目以降): Action 内部の処理は実行されず、保存されたキャッシュデータがレスポンスとして利用される。

といったことが実現できます。その結果、レスポンスを高速化できるわけです。

さて、そんな便利な Action Caching なのですが、ある日のこと「特定のアクションに対してAction Cachingが効かない」というトラブルが発生してしまいました。

何度 Action を呼び出しても Action 内部の処理が実行されてしまうんです。つまり、レスポンスのデータがきちんとキャッシュできていないということ。

このままだとダメなので、しっかり原因を調べてみることにしました。

原因

Railsのログファイルには、レスポンスのデータがレンダリングされたあとに、以下のようなログが出力されていました。(Cacheキーはダミー)

これだけ見るとキャッシュはデータは保存できているみたいなんですが、実際のところはできてない。Rails consoleを起動して、Rails.cache.read 'views/xxxx/yyyy' と実行しても nil が返ってくるだけ。

困った僕は、やけくそになって、memcached クライアントである dalli のバージョンを最新版にアップデート(2.5.0 → 2.6.4)してみました。

すると、ログファイルに、さっきまで出力されていなかった以下のようなログが出力されるように。

なんと、キャッシュとして保存しようとしていたデータのサイズが、memcached に保存できる1オブジェクトあたりの最大サイズ(最大キャッシュオブジェクトサイズ)を超えていたようです。

memcachedのデフォルトでは、最大サイズは1MBに設定されている模様。レスポンスのデータが1MBを超えていたので、キャッシュが保存できていなかった、というのが今回のトラブルの原因だったようです。

解決

今回は、「データを圧縮してから memcached に保存する」ことで対応することにしました。

幸い、dalli にデータを圧縮してから保存するためのオプションが用意されていたので、それを使うことに。

みたいな感じで、 { compress: true} というオプションを渡してやればOKです。

設定変更後は、無事にキャッシュが動作することを確認できました。めでたし。

まとめ

今回得た教訓は、

  • memcached には最大キャッシュオブジェクトサイズという概念がある
  • Gemのバージョンによっては必要なログが出力されないことがある
  • バージョンアップすると出力されるようになることがある

といった感じでしょうか。もしかすると dalli をバージョンアップしなくても、ログ出力オプションを変更すればなんとかなったのかもしれません。

まあ色々ありましたが、これで Action Caching がうまく動作するようになりました。こんなメモですが、どこかの誰かの参考になれば!

それではー。