Frage

Nehmen wir an, Sie haben ein Fragment der Seite, auf dem die neuesten Beiträge angezeigt werden, und Sie verfallen es in 30 Minuten. Ich benutze hier Rails.

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

Offensichtlich müssen Sie die Datenbanksuche nicht durchführen, um die neuesten Beiträge zu erhalten, wenn das Fragment vorhanden ist. Sie sollten also auch diesen Overhead vermeiden können.

Was ich jetzt mache, ist so etwas im Controller, der zu funktionieren scheint:

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

Ist das der beste Weg? Ist es sicher?

Eine Sache, die ich nicht verstehe, ist, warum der Schlüssel ist "recent_posts"Für das Fragment und"views/recent_posts"Beim später nachcheck memcached -vv um zu sehen, was es benutzte. Außerdem mag ich die Duplizierung nicht manuell eindringen "recent_posts"Es wäre besser, das an einem Ort zu behalten.

Ideen?

War es hilfreich?

Lösung

Evan Weaver Interlock -Plugin Löst dieses Problem.

Sie können so etwas auch selbst leicht implementieren, wenn Sie ein anderes Verhalten benötigen, z. B. eine feinkörnigere Kontrolle. Die Grundidee besteht darin, Ihren Controller -Code in einen Block zu wickeln, der nur dann ausgeführt wird, wenn die Ansicht diese Daten benötigt:

# 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

Wenn Sie mit den Grundlagen des Ruby -Meta -Programmierens vertraut sind, ist es einfach genug, dies in einer saubereren API Ihres Geschmacks zu füllen.

Dies ist überlegen, den Findercode direkt in die Ansicht zu setzen:

  • Hält den Findercode, in dem Entwickler ihn nach Übereinkommen erwarten
  • Hält die Ansicht nicht über den Modellnamen/die Modellname/die Methode des Modells unwissend, und ermöglicht eine weitere Wiederverwendung von Ansicht

Ich denke, Cache_FU könnte eine ähnliche Funktionalität in einer seiner Versionen/Gabeln haben, kann sich aber nicht speziell erinnern.

Der Vorteil, den Sie von Memcached erhalten, hängt direkt mit Ihrem Cache -Hit -Rate zusammen. Achten Sie darauf, dass Sie Ihre Cache -Kapazität nicht verschwenden und unnötige Fehlschläge verursachen, indem Sie denselben Inhalt mehrmals zwischengespeichert. Speichern Sie beispielsweise keine Reihe von Datensatzobjekten sowie ihr HTML -Fragment gleichzeitig. Im Allgemeinen bietet Fragment -Caching die beste Leistung, hängt jedoch wirklich von den Besonderheiten Ihrer Anwendung ab.

Andere Tipps

Was passiert, wenn der Cache zwischen dem Zeitpunkt, an dem Sie im Controller darauf suchen, und dem Zeitpunkt, an dem er in der Ansichts -Rendering überprüft wird, abläuft?

Ich würde eine neue Methode im Modell erstellen:

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

Verwenden Sie das dann in der Ansicht:

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

Aus Gründen der Klarheit können Sie auch in Betracht ziehen, die Darstellung eines kürzlich veröffentlichten Beitrags in einen eigenen Teil zu verschieben:

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  <%= render :partial => "recent_post", :collection => Post.recent(20) %>
<% end %>

Möglicherweise möchten Sie auch untersuchen

Fragment -Cache -Dokumente

Das ermöglicht es Ihnen, dies zu tun:

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

Regler

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

Obwohl ich zu gebe, dass das Problem der Trocken immer noch den Kopf aufnimmt und den Namen des Schlüssels an zwei Stellen benötigt. Ich mache dies normalerweise ähnlich wie Lars vorschlug, aber es hängt wirklich vom Geschmack ab. Andere Entwickler, von denen ich kenne, dass es sich um das Überprüfen von Fragment handelt.

Aktualisieren:

Wenn Sie sich die Fragment -Dokumente ansehen, können Sie sehen, wie es das Präfix der Ansicht benötigt:

# 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

Lars macht einen wirklich guten Punkt darüber, dass es eine leichte Chance auf Versagen gibt, mithilfe voneinander zu versagen:

unless fragment_exist?("recent_posts")

Denn eine Lücke zwischen dem Überprüfen des Cache und der Verwendung des Cache.

Das Plugin, das Jason erwähnt (Interlock), behandelt dies sehr anmutig, indem Sie davon ausgehen, dass Sie das Fragment wahrscheinlich auch verwenden, wenn Sie nach Existenz des Fragments prüfen und so den Inhalt lokal zwischengespeichert. Ich benutze Interlock aus genau diesen Gründen.

Genau wie ein Gedanke:

im Anwendungscontroller definieren

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

dann in jeder Aktion Verwendung

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

im Hinblick auf

cache "cache_key" do
view_code
end
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top