Akata Works

東京エンジニア。主にRuby,Go,たまにAWSとiOS。ゲーム音楽が好きです。連絡はTwitterかakata.onen@gmail.comまで

Railsでオンメモリにキャッシュするとハマるので注意

Railsのフラグメントキャッシュでオンメモリを指定してたら、外部ミドルウェアを使うのと挙動が違ってハマったのでメモ。
開発環境デフォルトのオンメモリは準備が楽ですが、本番でDalliとか使ってると混乱するので統一しておきたいですね。

検証

恐らく最もよく使われると思われるRails.cache.fetchの結果をそれぞれ見てみます。

Dalli(Memcached)

設定値config.cache_store = :mem_cache_store

[1] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
Dalli::Server#connect localhost:11211
no cache
=> {:hoge=>"huga"}
[2] pry(main)> @tmp[:foo] = 'bar'
=> "bar"
[3] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
=> {:hoge=>"huga"}
[4] pry(main)> @tmp[:foo] = 'bar'
=> "bar"
[5] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
=> {:hoge=>"huga"}

on Memory

設定値config.cache_store = :memory_store

[1] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
no cache
=> {:hoge=>"huga"}
[2] pry(main)> @tmp[:foo] = 'bar'
=> "bar"
[3] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
=> {:hoge=>"huga"}
[4] pry(main)> @tmp[:foo] = 'bar'
=> "bar"
[5] pry(main)> @tmp = Rails.cache.fetch('hoge'){puts 'no cache'; {hoge: 'huga'}}
=> {:hoge=>"huga", :foo=>"bar"}

このようにオンメモリではキャッシュされた値に、その後の変更が適応されています。

原因

恐らく、オンメモリだとrailsプロセスと同一のメモリ空間に保存されるため、ポインタのその後の変更も適応されているんだと思います。三回目の呼び出しまで適応されない理由はわかりませんが......(ドキュメントとかコードちゃんと読んだ人教えて!)

結論

あんまりオンメモリストア使うもんじゃないね。