带Memcache的Rails返回错误的缓存对象?

时间:2009-04-09 15:11:04

标签: ruby-on-rails ruby caching memcached

我有一个相当大的Rails应用程序,它在单独的服务器上使用memcached作为其缓存存储。

问题是我在似乎的生产环境中随机出现错误,表明memcached正在返回一个不正确的对象。

示例:

在此示例中,current_site是一个帮助方法,它访问Site模型上使用Rails.cache缓存模型的方法

ActionView::TemplateError in ListingsController#edit
undefined method `settings' for #<String:0xb565f8a0>

On line #12 of app/views/layouts/site.html.erb

    9:         <meta name="robots" content="noodp, all" />
    10:         <meta name="distribution" content="Global" />
    11: 
    12:         <% unless current_site.settings[:google_webmaster_verification_code].blank? %>
    13:         <meta name="verify-v1" content="<%= current_site.settings[:google_webmaster_verification_code] %>" />
    14:         <% end %>
    15: 

与......相比。

ActionView::TemplateError in ApplicationController#not_found
undefined method `settings' for #<Category:0xd5c6c34>

On line #12 of app/views/layouts/site.html.erb

    9:         <meta name="robots" content="noodp, all" />
    10:         <meta name="distribution" content="Global" />
    11: 
    12:         <% unless current_site.settings[:google_webmaster_verification_code].blank? %>
    13:         <meta name="verify-v1" content="<%= current_site.settings[:google_webmaster_verification_code] %>" />
    14:         <% end %>
    15: 

两者都应该返回Site模型!

缓存的另一个例子表现奇怪:

ActionView::TemplateError in AccountsController#show
can't convert Category into String

On line #141 of app/views/layouts/site.html.erb

    138:                    <li<%=  class="first" if i == 0 %>><%= link_to top_level_category.title, top_level_category.path %></li><% end %>
    139:                </ul>
    140:            <% end %>
    141:            <% cache bottom_pages do %>
    142:                <ul><% Page.top_level.active.show_in_navigation.find(:all, :include => :slugs).each_with_index do |top_level_page, i| %>
    143:                    <li<%=  class="first" if i == 0 %>><%= link_to top_level_page.title, top_level_page.path %></li><% end %>
    144:                </ul>

以前有没有人遇到这样的事情?任何人都有想法诊断这个无法解释的问题!?我已经尝试切换出memcached客户端宝石,想想也许这是一个奇怪的错误,但这没有任何影响!感谢。

5 个答案:

答案 0 :(得分:10)

这是由Passenger共享其与Memcached服务器的连接引起的。检查http://www.modrails.com/documentation/Users%20guide.html#_example_1_memcached_connection_sharing_harmful

修复只是将Passenger的Rails生成更改为conservative

答案 1 :(得分:1)

可能会有所帮助的一些事情:

  • 将检测/日志记录添加到current_site,以确切了解要返回的内容。
  • 如何在memcache中指定密钥?您可能会意外地在两个不同的位置为两个不同的对象使用相同的密钥。
  • 使用memcached-tool host:port dump > /tmp/keys查看memcache中的实际内容。
  • 你的memcached是在防火墙后面而不是在公共IP上曝光,对吗?

答案 2 :(得分:1)

我也遇到了这个问题,并且解决了在缓存提供程序进行解组操作之前为每个类/模型添加require_dependency。也许在生产环境中这不是必需的,因为选项:config.cache_class设置为true,但在测试和开发中它是错误的。

实际上,Memcache(我正在使用的缓存提供程序)找不到引用的类来执行unmarshal,然后引发此错误。

在这篇文章中有一个更好的解决方案来解决这个问题: http://kballcodes.com/2009/09/05/rails-memcached-a-better-solution-to-the-undefined-classmodule-problem/

问候!

答案 3 :(得分:0)

是的,我已经发生了这种情况。对我来说,这是因为我正在做Rails.cache.fetch(key)并且密钥是空白的。

我已经在Rails控制台中完成了一些操作,并使用以下内容:

Rails.cache.read validkey  # Get back the proper data
Rails.cache.fetch('') { 'abc' } # Error in rails log: 'MemCacheError ():'
Rails.cache.read validkey # Get back nil
Rails.cache.read validkey # May get back proper data

答案 4 :(得分:0)

在此处添加评论以防其他人出现... kballcodes.com网址不再有效(尽管您仍然可以通过archive.org访问它)。在该博客文章的评论中,有人描述了一种方法,使Marshal尝试加载有问题的对象,如果它首先抛出'undefined class / module'错误。我在下面包含了该代码,并在代码示例下引用了原作者。

将其添加到RAILS_ROOT / config / initializers /文件夹中的初始化文件中:

# 
# Marshal.load is a C-method built into Ruby; because it's so low-level, it
# bypasses the full classloading chain in Ruby, in particular the #const_missing
# hook that Rails uses to auto-load classes as they're referenced. This monkey
# patch catches the generated exceptions, parses the message to determine the
# offending constant name, loads the constant, and tries again.
#
# This solution is adapted from here:
# http://kballcodes.com/2009/09/05/rails-memcached-a-better-solution-to-the-undefined-classmodule-problem/
#
class <<Marshal
  def load_with_rails_classloader(*args)
    begin
      load_without_rails_classloader(*args)
    rescue ArgumentError, NameError => e
      if e.message =~ %r(undefined class/module)
        const = e.message.split(' ').last
        const.constantize
        retry
      else
        raise(e)
      end
    end
  end

  alias_method_chain :load, :rails_classloader
end

所有这一点归功于Matt Brownthis code as a pastie并对上述现已发表的文章发表了评论: