Rails with Memcache returning wrong cached object?

≯℡__Kan透↙ 提交于 2019-12-23 09:46:30

问题


I have a fairly large Rails app, which uses memcached on a seperate server as its cache store.

The problem is that I randomly get errors in the production environment which seem to indicate that memcached is returning an incorrect object.

Examples:

In this example, current_site is a helper method which accesses a method on the Site model that uses Rails.cache to cache the model

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: 

contrasted with....

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: 

When both should be returning a Site model!

Another example of cache behaving strangely:

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>

Has anyone encountered something like this before? Anyone have thoughts on diagnosing this unreplicable problem!? I've tried switching out memcached client gems, thinking maybe it was a weird bug, but this didn't have any effect! Thanks.


回答1:


This was being caused by Passenger sharing its connection to the Memcached server. Check http://www.modrails.com/documentation/Users%20guide.html#_example_1_memcached_connection_sharing_harmful.

The fix was simply to change Passenger's Rails spawn to conservative.




回答2:


A few things that might help:

  • Add instrumentation/logging to current_site to see exactly what is being returned.
  • How are you specifying keys in memcache? You could accidentally be using the same key in two different places for two different objects.
  • Use memcached-tool host:port dump > /tmp/keys to look at what's actually in your memcache.
  • Your memcached is behind a firewall and not exposed on a public IP, right?



回答3:


I had this problems too and was solved adding require_dependency for every class/model before the unmarshal operation by the cache provider. Maybe in production environment this is not necessary because the option: config.cache_class is setted to true, but in test and development it's false.

In fact, the Memcache (is the cache provider that I'm using) don't find the referenced class to do the unmarshal and then raise this error.

In this post has a little better solution to this problem: http://kballcodes.com/2009/09/05/rails-memcached-a-better-solution-to-the-undefined-classmodule-problem/

Regards!




回答4:


Yes, I've had this happen. With me, it was because I was doing Rails.cache.fetch(key) and key was blank.

I've done some playing around in the Rails console and with the following:

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



回答5:


adding a comment to this in case anyone else comes along... the kballcodes.com url is no longer valid (although you can still access it via archive.org). In the comments on that blog post, someone described a way to make Marshal try loading the object in question if it first throws the 'undefined class/module' error. I have included that code below, and referenced the original author beneath the code sample.

Add this to an initializer file in your RAILS_ROOT/config/initializers/ folder:

# 
# 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

All credit for this goes to Matt Brown who made this code as a pastie and commented on the now-dead article above:



来源:https://stackoverflow.com/questions/734664/rails-with-memcache-returning-wrong-cached-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!