Rails 4.0 expire_fragment/cache过期不起作用



我一直在尝试使用rails的缓存功能,但我无法使一些缓存片段过期,尽管它们似乎过期了。使用rails教程网站中指出的"俄罗斯娃娃缓存",我正在使用此配置

<% cache "all_available_releases" do %>
 <% @releases.each do |release| %>
  <% cache(release) do %>
   <html code with>
   <%ruby code @release.name blah blah blah%>
  <%end%>
 <%end%>
<%end%>    

我使release_controller.rb控制器中的外部缓存过期,在这里我使用expire_fragment("all_available_releases")使片段过期。我在控制器的每一个更新、删除或添加条目的方法中都使用它。

这是WEBrick的日志,尽管过期片段被注册,但5行后,过期片段被读取并使用,而它不应该被使用。此示例是在destroy调用之后。

Processing by ReleasesController#destroy as HTML
  Parameters: {"authenticity_token"=>"***/***/********************+********=", "id"=>"2"}
  Release Load (0.1ms)  SELECT "releases".* FROM "releases" WHERE "releases"."id" = ? LIMIT 1  [["id", "2"]]
   (0.1ms)  begin transaction
  SQL (2.0ms)  DELETE FROM "releases" WHERE "releases"."id" = ?  [["id", 2]]
   (148.0ms)  commit transaction
Expire fragment views/all_available_releases (0.1ms)
Redirected to http://127.0.0.1:3000/releases
Completed 302 Found in 180ms (ActiveRecord: 150.2ms)

Started GET "/releases" for 127.0.0.1 at 2013-07-03 13:09:51 +0300
Processing by ReleasesController#index as HTML
Read fragment views/all_available_releases/41cb0a928326986f35f41c52bb3d8352 (0.1ms)
  Rendered releases/index.html.erb within layouts/application (0.6ms)
Completed 200 OK in 5ms (Views: 4.0ms | ActiveRecord: 0.0ms)

我甚至尝试过使用Rails.cache.delete("all_available_releases"),但它也不起作用。

如果我从我的html.erb中删除<%cache "all_available_releases"%>(和一个<%end%>),那么缓存工作正常,并且在应该过期的时候就会过期。

我认为问题是,当您在视图中缓存片段时,缓存摘要会添加到缓存密钥中(views/all_available_releases/41cb0a928326986f35f41c52bb3d8352),但expire_fragment没有使用摘要(views/all_availale_releases)。

如果将skip_digest: true添加到视图中的缓存调用中,则应防止使用摘要。

<% cache "all_available_releases", skip_digest: true do %>
 <% @releases.each do |release| %>
  <% cache(release) do %>
   <html code with>
   <%ruby code @release.name blah blah blah%>
  <%end%>
 <%end%>
<%end%>

缓存摘要仅用于自动缓存过期。如果需要手动使缓存密钥过期,则不能使用缓存摘要。

Jbuilder不支持skip_digest。在经历了许多失败的方法之后,我决定在这里分享我的答案,因为它与rails观点高度相关,尽管不像上面的问题那样。

这里有一个相关的Q/问题,DHH本质上告诉这个家伙,他不能显式地终止fragment_caches。https://github.com/rails/cache_digests/issues/35一切都不平衡,所以有一种方法可以解决这个问题:

class MenuController
  def index
    json = Rails.cache.fetch('clients') do
      @items = Menu.all
      render_to_string( template: 'menu/index', locals: {items: @items})
    end
    render json: json
  end
end

然后你可以在任何地方明确地终止它,比如在观察者中

class MenuCacheObserver < ActiveRecord::Observer
  observe :menu, :menuitem, :menusubnavigation
  def after_save obj
    Rails.cache.delete(:clients)
  end
end

在少数情况下,这可能是有道理的。一般来说,在大多数情况下,您应该在缓存输入中使用对象,如包装jbuilder视图的json.cache! @my_object do。这样,当对象上的updated_at发生更改时,它就会失效。

我自己刚刚遇到这个问题,我处理这个问题的方法是通过正则表达式。这可能不是最优雅的解决方案,但效果很好。

ActionController::Base.new.expire_fragment(%r{offer_#{@offer.id}/*})

不过,添加skip_digest要好得多。

在Rails 5中,我采取了以下步骤来破坏缓存,而不使用skip_digest: true。我们的问题是,更改I18n字符串的值不会反映在计算的缓存摘要中,因此缓存不会自动中断。

以下是定义缓存块的视图:

/ views/layouts/_footer.html.slim
- cache :footer do
  span= t('shared.footer')

然后在轨道控制台中运行:

fragment = ActionController::Base.new.view_context.cache_fragment_name(:footer, virtual_path: 'layouts/_footer.html.slim')
ActionController::Base.new.expire_fragment(fragment)

cache_fragment_name将根据virtual_path关键字参数计算摘要。

相关内容

  • 没有找到相关文章

最新更新