ActiveSupport::Cacheの豆知識
はじめに
ActiveSupport::Cache は、Rails.cache.read, Rails.cache.writeと書くことでフラグメントキャッシュを扱うクラスです。 さきほど、このクラスのソースコードを読んだので知見を紹介します。(rails4.2時点)
Rails.cache.fetchにブロックを渡すとミスキャッシュした時に書き込んでくれる
これは公式ドキュメントに書いている通り有名だと思います。
# cache = ActiveSupport::Cache::MemCacheStore.new # cache.fetch("foo", force: true, raw: true) do # :bar # end
エントリがなかったら save_block_result_to_cache
というメソッドが呼ばれていました。
複数エントリを一括に読み書きするメソッドがある
3.times do |i| Rails.cache.write("a#{i}", "はい#{i}") end
という3回ストアへ書き込むコードがありますが、これを1度のアクセスだけで行うメソッドが存在しています。
Rails.cache.write_multi
, Rails.cache.read_multi
です。
Rails.cache.write_multi(3.times.map {|i| { "a#{i}" => "はい#{i}" } })
な感じです。
このインターフェースはAPIドキュメントにも記載されている通り、スーパークラスで定義していますが、本当に1度だけ書き込むのかはキャッシュストアに依存した実装になっています。 RedisCacheStoreでは、ちゃんと1度だけ書き込むようになっていました。
# Writes multiple entries to the cache implementation. Subclasses MAY # implement this method. def write_multi_entries ...
キャッシュストアのクラス構成は継承を使っている。
ActiveSupport::Cacheをスーパークラスとして、各キャッシュストアはサブクラスとして定義されています。
これは、共通のIFを持たせたかったためでしょう。
raise NotImplementedError
というメソッドがスーパークラスの随所で散見されます。
キャッシュストア内からストアの操作には instrument
というラッパーメソッドを使わなければならない
def write(name, value, options = nil) options = merged_options(options) instrument(:write, name, options) do entry = Entry.new(value, **options.merge(version: normalize_version(name, options))) write_entry(normalize_key(name, options), entry, **options) end end
instrumentというラッパーの中には ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
がコールされていました。
なるほど〜これは、Notificationのsubscriberに通知をするためだったんですね。
instrumentを使わずに操作をしている場合は、subscriberへの通知もれが起きるでしょう。
むすび
そこまで突っ込んで読んではいませんが、全体の構成がわかってきたように思います。
おわり