トップ «前の日(10-29) 最新 次の日(10-31)» 追記   RSS 1.0 FEED  

Journal InTime


2001-10-30 (Tue)

_ ねむい

3時間おきに起こされるってのはまったく拷問ですね。


2002-10-30 (Wed)

_ SPAMフィルタ

ruby-talk MLにSPAMフィルタを導入。

テストでSPAMっぽいメールを流したらSPAMフィルタにSPAMと認識されなくて MLに流れてしまった:-(

お願いですから苦情のメールを私に送ってこないでくださいm(..)m (って日本語で書いても意味ないだろうな。)


2003-10-30 (Thu)

_ 子供

陣痛がはじまって42時間後にようやく誕生。 誰だよ、二人目は早いなんて言ったやつは。


2004-10-30 (Sat)

_ 長男の誕生日

長女の時は旅館に泊まったりしたのだが、家でケーキを食べただけ。 ごめんよ。来年はもうちょっと何とかするからな。

Tags: その他

_ オフハウス

花瓶と子どものおもちゃをポットと売りに行った。 合計1400円なり。

明細のポットの部分のジャンルが「ジャンク」になっていたのがせつない。もうちょっと他に書き方がないものか。

Tags: その他

2013-10-30 (Wed)

_ ロシア人形キャッシュとキャッシュダイジェスト

Russian-doll Caching (Cache Digests) は、Rails 4 に導入された機能です。これは、複数のフラグメントキャッシュを入れ子にしている場合に、内側のコンテンツの更新に連動して、外側のフラグメントキャッシュのキーが自動的に変更されるというものです。

[Rails アプリケーションのパフォーマンスについて RubyKaigi 2013 で発表しましたより引用]

という記事を見て、内側のフラグメントキャッシュ内で使用されているモデルが更新される場合も、外側のキャッシュのキーが自動的に更新されるのかなと勘違いしたけど、そんなわけはなかったのでメモ。

ロシア人形キャッシュ

まず、ロシア人形キャッシュというのは、

<% cache @article do %>
<h1><%= @article.title %></h1>
<p><%= @article.body %></p>
<h2>コメント</h2>
<%= render @article.comments %>
<% end %>

のようなフラグメントキャッシュがあった時に、キャッシュの中で使用しているテンプレート(comments/_comment.html.erb)でも

<% cache comment do %>
<p><%= comment.body %></p>
<p>by <%= comment.name %> at <%= comment.created_at %></p>
<% end %>

のようにフラグメントキャッシュを使うこと。こうすると、外側のキャッシュが無効になった時も内側のキャッシュ(の一部)は有効なことが多いので、更新された場合をレンダリングするだけで済む。

モデル更新時のキャッシュの無効化

内側のキャッシュが無効になった場合は、外側のキャッシュを無効にする必要があるが、belongs_toのtouchオプションを使うと子モデルが更新された時に親モデルのupdated_atを自動的に更新できる(これはRails 3でも使える機能)。

class Article < ActiveRecord::Base
  has_many :comments, dependent: :destroy
end

class Comment < ActiveRecord::Base
  belongs_to :article, touch: true
end

キャッシュのキーはupdated_atを元に生成されるので、updated_atが更新されると古いキャッシュは無効になる(ヒットしなくなる)。

テンプレート更新時のキャッシュの無効化

問題はテンプレートファイルを更新した時で、モデルのデータは変わっていなくても、キャッシュを無効にする必要がある。

従来は、

<% cache ["v1", comment] do %>
…
<% end %>

のようにcacheの引数にテンプレートのバージョンを含めて対応することができた。 フラグメントキャッシュは引数のオブジェクト全体からキャッシュキーを生成するので、commentの方に変更がなくても、"v1"の部分を変えてやればキャッシュキーが変わり、古いキャッシュが無効になる。 ただ、手動でやるのはいかにも面倒くさいし、このテンプレートに依存する他のテンプレート(この場合はarticles/show.html.erb)の方のバージョンを更新するのを忘れるリスクがある。

キャッシュダイジェスト

Rails 4では自動的にテンプレートファイルのダイジェスト値がキャッシュキーに追加されるのでこういったことをする必要がなくなった(これがキャッシュダイジェストという新機能)。 また、テンプレートの依存関係を把握していて、内側のテンプレートが変更された場合も外側のキャッシュのキーが自動的に変更される。

注意点

上記のように賢い機能だが、

<%= render @project.documents.where(published: true).order('created_at') %>

のような複雑なケースだと依存関係を自動では認識してくれないようなので注意が必要。

render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')

のようにpartialオプションでテンプレートを明示したり、

<%# Template Dependency: documents/document %>

のようにコメントで依存関係を明示すれば大丈夫らしいけど、render partial:とか冗長だよねとか言ってリファクタリングしてもテストは通るからハマリそうな気がする(Railsってこういう罠が多い気が)。

あと、キャッシュデータそのものは勝手に消えるわけではないので、例えばMemCacheStoreを使っている場合は、以下のようにexpireする時間を指定するなどの工夫が必要になる*1

config.cache_store = :mem_cache_store, "cache1.example.com", "cache2.example.com", {expires_in: 3.hours}
Tags: Ruby Rails

*1  Rails 4のMemCacheStoreではmemcache-clientの代りにDalliが使われるようになったのでexpireの指定ができる。