10

我正在开发一个 Rails 应用程序,我在其中使用页面缓存来存储静态 html 输出。缓存工作正常。不过,我在使缓存过期时遇到问题。

我相信我的问题部分是因为我没有使控制器中的缓存过期。为此所需的所有操作都在模型中处理。这似乎应该是可行的,但是我发现的所有对基于模型的缓存过期的引用似乎都已过时,或者无法正常工作。

在我的 environment.rb 文件中,我正在调用

config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )

我在 /sweepers 文件夹中有一个 LinkSweeper 文件:

class LinkSweeper < ActionController::Caching::Sweeper
  observe Link

  def after_update(link)
    clear_links_cache(link)
  end

  def clear_links_cache(link)
  # expire_page :controller => 'links', :action => 'show', :md5 => link.md5
    expire_page '/l/'+ link.md5 + '.html'
  end
end

那么......为什么在我更新模型时它不删除缓存页面?(过程:使用脚本/控制台,我正在从数据库中选择项目并保存它们,但它们的相应页面并未从缓存中删除),并且我还在调用通常会调用的 Link 模型中的特定方法扫地机。两者都不起作用。

如果重要,缓存文件是链接表中键值的 md5 散列。缓存页面存储为 /l/45ed4aade64d427...99919cba2bd90f.html 之类的内容。

从本质上讲,Sweeper 似乎并没有真正观察 Link。我还读到(这里)可能可以简单地将清扫器添加到 environment.rb 中的 config.active_record.observers,但这似乎没有这样做(而且我不确定 app/sweepers 的 load_path 是否在 environment.rb 中避免了这一点)。

4

7 回答 7

11

所以我尝试了许多不同的方法,看看哪些有效,哪些无效。

再次总结一下情况:我的目标是在对象更新时使缓存页面过期,但在不依赖控制器操作的情况下使它们过期。传统的清扫机使用控制器中的一条线来通知清扫机它需要运行。在这种情况下,我不能在控制器中使用一条线,因为更新是在模型中进行的。普通的清扫器教程不起作用,因为它们假定您与数据库对象的主要交互是通过控制器进行的。

如果在阅读本文时,您发现了一种收紧我的代码的方法,请发表评论并告诉我。

首先,让我们看看确实有效的东西,以防你也被困在这个问题上并需要帮助。

在我尝试过的所有事情中,唯一似乎真正起作用的是在模型的观察者中声明一个 after_update 命令。在该命令中,我对 expire_page 操作使用了显式命令,并包含了在 routes.rb 中声明的路径。

所以。这有效:

在 config/routes.rb 中:

map.link 'l/:md5.:format',  :controller => 'links', :action => 'show'

在 app/models/link_observer.rb 中:

def after_update(link)
  ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
end

请注意,“md5”特定于我的应用程序。您可能想要使用 :id 或其他一些唯一标识符。

我还发现,从进行更新的模型中的方法中声明 ActionController::Base... 行是有效的。也就是说,在 Link.rb 中,在实际更新数据库的方法中,如果我只是将整行插入,它就起作用了。但由于我可能希望将来在其他方法上使该页面缓存过期,我宁愿将它提取到观察者中。

现在,让我们看看一些不起作用的东西,以防你在谷歌上搜索。

在 link_observer.rb 的 after_update(link) 方法中调用“expire_page(...)”不起作用,因为它返回了“未定义的方法 `expire_page'”错误

创建一个观察模型的 Sweeper 文件不起作用。我找不到任何错误代码,但它似乎甚至不知道它有工作要做。这是在 environment.rb 中显式调用“config.load_paths += %W(#{RAILS_ROOT}/app/sweepers)”之后。以防万一我在该代码中添加了一些东西,这里是:

class LinkSweeper < ActionController::Caching::Sweeper
  observe Link

  def after_update(link)
    clear_links_cache(link)
  end

  def clear_links_cache(link)
    # DID NOT WORK    expire_page :controller => 'links', :action => 'show', :md5 => link.md5
    # DID NOT WORK    expire_page '/l/'+ link.md5 + '.html'
    # DID NOT WORK    ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
  end
end

上面的示例在目录 /app/sweepers 中有 link_sweeper.rb 文件。我还尝试将 link_sweeper.rb 放在 app/models 目录中,并尝试使用 environment.rb 中的 config.active_record.observers 命令调用它:

config.active_record.observers = :link_observer, :link_sweeper

但这也不起作用。

是的。这些方法中的一种很可能会起作用,并且我在代码中搞砸了一些东西。但我认为我做的一切都是照本宣科的。

最后,总结一下:您希望在模型的 Observer 中设置一个 after_ 回调,而不是使用 Sweeper 来过期页面缓存。您需要使用 Base.expire_page 方法的显式路径:

def after_update(<model>) # where <model> is the name of the model you're observing
  ActionController::Base.expire_page(app.<model>_path(:id => <model>.id)) # where <model> is the name of the model you're observing
end

希望这会帮助其他人。同样,如果您在我的无效代码中看到我应该做一些不同的事情,请告诉我。如果您在我的工作代码中看到一些更严格的内容,也请告诉我。

于 2009-09-23T13:10:37.477 回答
5

请注意:您可以cache_sweeper在 ApplicationController 中使用。

class ApplicationController < ActionController::Base
  cache_sweeper :my_sweeper
end

class MySweeper < ActionController::Caching::Sweeper
  observe MyModel

  def after_update(my_model)
    expire_page(...)
  end
end
于 2010-06-07T08:07:14.777 回答
3

我在尝试进行片段缓存(rails 3)时遇到了同样的问题。无法让扫地机进行观察,因此我决定采用上述解决方案使其成为 AR 观察者并调用ApplicationController.new.expire_fragment(...).

于 2011-01-07T12:46:13.983 回答
2

我确实得到了这个工作。我的设置中唯一的细微差别是清扫器是 Rails 引擎的一部分。这导致了细微的差异(在引擎的 init 中使用 require 加载清扫器文件,而不是将其添加到 environment.rb 中的加载路径等)。

因此,清扫器被加载到引擎的 init.rb 中,如下所示:

require File.join(File.dirname(__FILE__), 'app', 'sweepers', cached_category_count_sweeper')

我称它为清扫器,因为它“清扫”缓存,但我猜它只是模型上的观察者:

class CachedCategoryCountSweeper < ActiveRecord::Observer
  observe CategoryFeature

  def before_save(cf)
    expire_cache(cf.category_id_was) if cf.category_id_changed?
  end

  def after_save(cf)
    expire_cache(cf.category_id)
  end

  def after_destroy(cf)
    expire_cache(cf.category_id)
  end

  def expire_cache(c)
    ApplicationController.expire_page("/categories/#{c}/counts.xml") if !c.nil?
  end
end

坦率地说,我不喜欢硬编码路径,但我尝试添加:

include ActionController:UrlWriter

然后使用路径方法,但它只在开发中对我有用。它在生产中不起作用,因为我的生产服务器使用相对 url 根(而不是虚拟主机)并且内部方法“page_cache_path”会始终使文件路径错误,因此它不会过期。

由于这是一个观察者,我在 environment.rb 中添加了:

config.active_record.observers = :cached_category_count_sweeper

最后是使用缓存的控制器(不会过期,这是通过模型观察者完成的):

class CachedCategoryCountsController < ApplicationController
  caches_page :index

  # GET /cached_category_counts.xml
  def index
    ...
  end
end

无论如何,希望这会有所帮助。

安德烈斯·蒙塔诺

于 2010-06-16T16:52:12.220 回答
0

通过添加,我已经能够让它工作

ActionController::Base.expire_page(app.link_path(:md5 => @link.md5))

到模型本身更新数据库的方法。不过,这感觉有点 hacky,我很想知道是否有人可以解释为什么它不适用于正常的清扫器设置,以及是否有更优雅的方法来处理这个问题。

那段代码(除了我为自己的应用程序进行的自定义)来自ruby​​-forum.com 上的这篇文章

于 2009-09-23T03:00:22.107 回答
0

我在这里写了一些关于这个主题的文章:Rails Cache Sweeper Confusion。很想听听你的意见。

于 2010-12-14T17:34:58.433 回答
0

基于@moiristo 和@ZoogieZork 的答案,我猜这会起作用(未经测试)。

class LinkSweeper < ActiveRecord::Observer
  include ActionController::Caching::Pages
  # or if you want to expire fragments
  #include ActionController::Caching::Fragments

  observe Link

  def after_update(link)
    expire_page( ... )
    #expire_fragment( ... )
  end
end
于 2012-01-13T09:06:21.313 回答