Question

Disons que vous avez un fragment de la page qui affiche les publications les plus récentes et que vous l'expirez au bout de 30 minutes. J'utilise Rails ici.

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

Évidemment, vous n'avez pas besoin de consulter la base de données pour obtenir les publications les plus récentes si le fragment existe, vous devriez donc pouvoir éviter cette surcharge.

Ce que je suis en train de faire ressemble à ceci dans le contrôleur, ce qui semble fonctionner:

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

Est-ce le meilleur moyen? Est-ce sécuritaire?

Une chose que je ne comprends pas, c'est pourquoi la clé est " Recent_posts ". pour le fragment et " views / recent_posts ". lors de la vérification ultérieure, mais je l’ai trouvée après avoir regardé memcached -vv pour voir ce qu’il utilisait. De plus, je n'aime pas la duplication consistant à entrer manuellement le code < Recent_posts ", il serait préférable de le conserver au même endroit.

Des idées?

Était-ce utile?

La solution

Le plug-in d'interlock d'Evan Weaver résout ce problème.

Vous pouvez également implémenter quelque chose comme cela vous-même facilement si vous avez besoin d'un comportement différent, tel qu'un contrôle plus fin. L'idée de base est d'envelopper le code de votre contrôleur dans un bloc qui n'est réellement exécuté que si la vue a besoin de ces données:

# 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

Si vous connaissez les bases de la méta-programmation Ruby, il est assez facile de l'intégrer dans une API plus propre à votre goût.

C’est mieux que de placer le code de recherche directement dans la vue:

  • conserve le code de recherche là où les développeurs l'attendent par convention
  • garde la vue ignorante du nom du modèle / de la méthode, permettant plus de réutilisation de la vue

Je pense que cache_fu pourrait avoir des fonctionnalités similaires dans l'une de ses versions / forks, mais ne peut pas en rappeler spécifiquement.

L'avantage que vous obtenez de memcached est directement lié à votre taux d'accès au cache. Veillez à ne pas gaspiller votre capacité de cache et à provoquer des ratés inutiles en mettant en cache le même contenu plusieurs fois. Par exemple, ne mettez pas en cache un ensemble d'objets record ainsi que leur fragment HTML en même temps. La mise en cache de fragments offre généralement les meilleures performances, mais cela dépend vraiment des spécificités de votre application.

Autres conseils

Que se passe-t-il si le cache expire entre le moment où vous le vérifiez dans le contrôleur? et le temps est-il vérifié dans le rendu de la vue?

Je ferais une nouvelle méthode dans le modèle:

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

puis utilisez-le dans la vue:

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

Par souci de clarté, vous pouvez également envisager de transférer le rendu d'un message récent dans son propre partiel:

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

Vous voudrez peut-être aussi vous pencher sur

Fragment Cache Docs

Ce qui vous permet de faire ceci:

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

Contrôleur

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

Même si j’admets que la question de DRY soulève toujours la question de la clé à deux endroits. Je fais habituellement la même chose que suggéré par Lars, mais cela dépend vraiment du goût. Il existe d’autres développeurs qui possèdent un fragment de vérification.

Mise à jour:

Si vous examinez la documentation des fragments, vous pouvez voir comment il est possible de se passer du préfixe de vue:

# 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 fait une très bonne remarque sur le fait qu’il existe un léger risque d’échec en utilisant:

unless fragment_exist?("recent_posts")

car il y a un intervalle entre le moment où vous vérifiez le cache et celui où vous utilisez le cache.

Le plugin que jason mentionne (Interlock) gère cela de façon très élégante en supposant que, si vous vérifiez l’existence du fragment, vous utiliserez probablement également le fragment et mettra donc le contenu en cache localement. J'utilise Interlock pour ces mêmes raisons.

juste comme une pensée:

dans le contrôleur d’application définir

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

puis dans toute action utiliser

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

en vue

cache "cache_key" do
view_code
end
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top