Rails Jbuilderのcacheのキーの挙動の調査メモ
概要
RailsでjsonAPIを提供しているプロジェクトで、Jbuilderを使ったときのcacheのkeyの挙動を調べた際のメモ。
Jbuilderでは以下のようにcacheを使うことができる。
この引数のkey
には、どのようなオブジェクトが入ってもいいようになっている。
json.cache!(key, expires_in: 5.minutes) do json.hoge :foo end
一部でActionController::Parameters
のインスタンスが用いられていた箇所があったが、
ActionController::Parameters
はRails5から to_unsafe_h
か permit
を使わないと、中身が無視されるような仕様になったので、
この場合どういう挙動をするのかがわからなかったので調べた。
結果としては、ActionController::Parameters
をそのまま使っても問題なかった。
※ 注
- リクエストパラメータがコントロールできない環境では、そもそも
ActionController::Parameters
を直接使うべきではない。 - 今回は内部のアプリのAPIで、リクエストパラメータが十分制限される環境なので、使っていた。
詳細
環境
実装
実装としては、以下のようになっており ActiveSupport::Cache.expand_cache_key
内の返り値が使われていた。
https://github.com/rails/jbuilder/blob/91c4eeed484abd28cada2628af1f45f3de0cb0f5/lib/jbuilder/jbuilder_template.rb#L150
def _cache_key(key, options) name_options = options.slice(:skip_digest, :virtual_path) key = _fragment_name_with_digest(key, name_options) if @context.respond_to?(:combined_fragment_cache_key) key = @context.combined_fragment_cache_key(key) else key = url_for(key).split('://', 2).last if ::Hash === key end ::ActiveSupport::Cache.expand_cache_key(key, :jbuilder) end def _fragment_name_with_digest(key, options) if @context.respond_to?(:cache_fragment_name) # Current compatibility, fragment_name_with_digest is private again and cache_fragment_name # should be used instead. @context.cache_fragment_name(key, options) elsif @context.respond_to?(:fragment_name_with_digest) # Backwards compatibility for period of time when fragment_name_with_digest was made public. @context.fragment_name_with_digest(key) else key end end
ActiveSupport::Cache.expand_cache_key
内の処理は以下のようになっていた。
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/cache.rb#L80
module ActiveSupport module Cache class << self def expand_cache_key(key, namespace = nil) expanded_cache_key = (namespace ? "#{namespace}/" : "").dup if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] expanded_cache_key << "#{prefix}/" end expanded_cache_key << retrieve_cache_key(key) expanded_cache_key end private def retrieve_cache_key(key) case when key.respond_to?(:cache_key_with_version) then key.cache_key_with_version when key.respond_to?(:cache_key) then key.cache_key when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a) else key.to_param end.to_s end end end end
to_a
した後に、 to_param
されて、以下のようになる。
params # { hoge: :aaa, foo: :bbb } ActiveSupport::Cache.expand_cache_key(params) # => "hoge/aaa/foo/bbb"