10

最新の投稿を表示するページのフラグメントがあり、30分で期限切れになったとします。ここではRailsを使用しています。

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  ...
<% end %>

明らかに、フラグメントが存在する場合、最新の投稿を取得するためにデータベースルックアップを実行する必要はないため、そのオーバーヘッドも回避できるはずです。

私が今していることは、動作しているように見えるコントローラーでこのようなものです:

unless Rails.cache.exist? "views/recent_posts"
  @posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end

これが最善の方法ですか?安全ですか?

recent_postsなぜフラグメントのキーが「」で、後でチェックするときに「」なのかわからないのですが、何を使っているのかviews/recent_posts見てみて思いついたんです。memcached -vvまた、手動で「」を入力する重複は好きではありませんrecent_posts。それを1か所にまとめたほうがよいでしょう。

アイデア?

4

5 に答える 5

12

EvanWeaverのInterlockプラグインはこの問題を解決します。

よりきめ細かい制御など、さまざまな動作が必要な場合は、このようなものを自分で簡単に実装することもできます。基本的な考え方は、ビューがそのデータを必要とする場合にのみ実際に実行されるブロックでコントローラーコードをラップすることです。

# in FooController#show
@foo_finder = lambda{ Foo.find_slow_stuff }

# in foo/show.html.erb
cache 'foo_slow_stuff' do
  @foo_finder.call.each do 
    ...
  end
end

ルビーメタプログラミングの基本に精通している場合は、これを好みのよりクリーンなAPIにまとめるのは簡単です。

これは、ファインダーコードをビューに直接配置するよりも優れています。

  • 開発者が慣例により期待する場所にファインダーコードを保持します
  • ビューがモデル名/メソッドを認識しないようにし、ビューをより多く再利用できるようにします

cache_fuは、そのバージョン/フォークの1つで同様の機能を備えている可能性があると思いますが、具体的に思い出せません。

memcachedから得られる利点は、キャッシュヒット率に直接関係しています。同じコンテンツを複数回キャッシュすることで、キャッシュ容量を無駄にしたり、不要なミスを引き起こしたりしないように注意してください。たとえば、レコードオブジェクトのセットとそれらのhtmlフラグメントを同時にキャッシュしないでください。通常、フラグメントキャッシングは最高のパフォーマンスを提供しますが、実際にはアプリケーションの詳細によって異なります。

于 2009-06-23T23:43:24.370 に答える
3

コントローラでキャッシュをチェックしてからビューレンダリングでキャッシュがチェックされるまでの間にキャッシュの有効期限が切れるとどうなりますか?

モデルに新しいメソッドを作成します。

  class Post
    def self.recent(count)
      find(:all, :limit=> count, :order=>"updated_at DESC")
    end
  end

次に、ビューでそれを使用します。

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  <% Post.recent(20).each do |post| %>
     ...
  <% end %>
<% end %>

わかりやすくするために、最近の投稿のレンダリングを独自の部分に移動することも検討できます。

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  <%= render :partial => "recent_post", :collection => Post.recent(20) %>
<% end %>
于 2009-06-23T20:29:04.590 に答える
1

Larsは、以下を使用して失敗する可能性がわずかにあることについて、非常に良い点を示しています。

unless fragment_exist?("recent_posts")

キャッシュをチェックするときとキャッシュを使用するときの間にギャップがあるためです。

jasonが言及しているプラ​​グイン(Interlock)は、フラグメントの存在をチェックしている場合、おそらくフラグメントも使用するため、コンテンツをローカルにキャッシュすると想定して、これを非常に適切に処理します。私はこれらの理由からインターロックを使用しています。

于 2009-07-10T05:48:55.523 に答える
1

考えの一部として:

アプリケーションコントローラで定義

def when_fragment_expired( name, time_options = nil )
        # idea of avoiding race conditions
        # downside: needs 2 cache lookups
        # in view we actually cache indefinetely 
        # but we expire with a 2nd fragment in the controller which is expired time based
        return if ActionController::Base.cache_store.exist?( 'fragments/' + name ) && ActionController::Base.cache_store.exist?( fragment_cache_key( name ) )

        # the time_fraqgment_cache uses different time options
        time_options = time_options - Time.now if time_options.is_a?( Time )

        # set an artificial fragment which expires after given time
        ActionController::Base.cache_store.write("fragments/" + name, 1, :expires_in => time_options )

        ActionController::Base.cache_store.delete( "views/"+name )        
        yield    
  end

その後、任意のアクションで使用

    def index
when_fragment_expired "cache_key", 5.minutes
@object = YourObject.expensive_operations
end
end

ビューで

cache "cache_key" do
view_code
end
于 2010-09-03T10:49:37.000 に答える
1

あなたも調べたいかもしれません

フラグメントキャッシュドキュメント

これにより、これを行うことができます。

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  ...
<% end %>

コントローラ

unless fragment_exist?("recent_posts")
  @posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end

私はDRYの問題がまだ頭を抱えていることを認めますが、2か所でキーの名前が必要です。私は通常、ラースが提案したのと同じようにこれを行いますが、それは本当に好みに依存します。私が知っている他の開発者は、フラグメントのチェックに固執しています。

アップデート:

フラグメントドキュメントを見ると、ビュープレフィックスが不要になる方法がわかります。

# File vendor/rails/actionpack/lib/action_controller/caching/fragments.rb, line 33
def fragment_cache_key(key)
  ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
于 2009-06-24T05:10:54.117 に答える