Ruby on rails Rails 3.1中的Rails.cache错误-TypeError:can';t使用默认进程转储哈希

Ruby on rails Rails 3.1中的Rails.cache错误-TypeError:can';t使用默认进程转储哈希,ruby-on-rails,ruby,ruby-on-rails-3,caching,memcached,Ruby On Rails,Ruby,Ruby On Rails 3,Caching,Memcached,我在3.1.0.rc4(ruby 1.9.2p180(2011-02-18修订版30909)[x86_64-darwin10])上遇到了Rails.cache方法的问题。代码在2.3.12(ruby 1.8.7(2011-02-18 patchlevel 334)[i686 linux],MBARI 0x8770,ruby Enterprise Edition 2011.03)的同一应用程序中运行良好,但在升级后开始返回错误。我还不知道为什么 尝试缓存具有多个作用域的对象时,似乎会发生此错误 此

我在3.1.0.rc4(ruby 1.9.2p180(2011-02-18修订版30909)[x86_64-darwin10])上遇到了Rails.cache方法的问题。代码在2.3.12(ruby 1.8.7(2011-02-18 patchlevel 334)[i686 linux],MBARI 0x8770,ruby Enterprise Edition 2011.03)的同一应用程序中运行良好,但在升级后开始返回错误。我还不知道为什么

尝试缓存具有多个作用域的对象时,似乎会发生此错误

此外,任何使用lambda的作用域都会失败,无论作用域有多少

我从这些模式中遇到了失败:

Rails.cache.fetch("keyname", :expires_in => 1.minute) do
    Model.scope_with_lambda
end


Rails.cache.fetch("keyname", :expires_in => 1.minute) do
    Model.scope.scope
end
这是我收到的错误:

TypeError: can't dump hash with default proc
    from /project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:627:in `dump'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:627:in `should_compress?'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:559:in `initialize'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:363:in `new'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:363:in `block in write'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:520:in `instrument'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:362:in `write'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:299:in `fetch'
    from (irb):62
    from /project/shared/bundled_gems/ruby/1.9.1/gems/railties-3.1.0.rc4/lib/rails/commands/console.rb:45:in `start'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/railties-3.1.0.rc4/lib/rails/commands/console.rb:8:in `start'
    from /project/shared/bundled_gems/ruby/1.9.1/gems/railties-3.1.0.rc4/lib/rails/commands.rb:40:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'
TypeError:无法使用默认进程转储哈希
from/project/shared/bundled_-gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_-support/cache.rb:627:in'dump'
from/project/shared/bundled_gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_support/cache.rb:627:in“应该压缩吗?”
来自/project/shared/bundled_-gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_-support/cache.rb:559:in'initialize'
from/project/shared/bundled_-gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_-support/cache.rb:363:in'new'
from/project/shared/bundled_-gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_-support/cache.rb:363:在“写块”中
from/project/shared/bundled_-gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_-support/cache.rb:520:in'instrument'
from/project/shared/bundled_-gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_-support/cache.rb:362:in'write'
from/project/shared/bundled_-gems/ruby/1.9.1/gems/activesupport-3.1.0.rc4/lib/active_-support/cache.rb:299:in'fetch'
起始(irb):62
from/project/shared/bundled_gems/ruby/1.9.1/gems/railties-3.1.0.rc4/lib/rails/commands/console.rb:45:in'start'
from/project/shared/bundled_gems/ruby/1.9.1/gems/railties-3.1.0.rc4/lib/rails/commands/console.rb:8:in'start'
from/project/shared/bundled_gems/ruby/1.9.1/gems/railties-3.1.0.rc4/lib/rails/commands.rb:40:in`'
来自脚本/rails:6:in'require'
来自脚本/rails:6:in`'
我尝试过使用:raw=>true选项作为替代选项,但这不起作用,因为Rails.cache.fetch块正在尝试缓存对象


有什么建议吗?提前谢谢

这可能有点冗长,但我不得不花一些时间研究Rails源代码,以了解缓存内部是如何工作的。把事情写下来有助于我的理解,我认为分享一些关于事情如何运作的笔记不会有什么坏处。如果你赶时间,跳到最后


为什么会这样 这是ActiveSupport中有问题的方法:

def should_compress?(value, options)
  if options[:compress] && value
    unless value.is_a?(Numeric)
      compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
      serialized_value = value.is_a?(String) ? value : Marshal.dump(value)
      return true if serialized_value.size >= compress_threshold   
    end
  end
  false  
end
注意
序列化的_值的赋值。如果您在
cache.rb
中浏览,您将看到它在对象进入缓存之前将对象序列化为字节字符串,然后再次封送以反序列化对象。压缩问题在这里并不重要,重要的是封送处理的使用

问题是:

某些对象无法转储:如果要转储的对象包括绑定、过程或方法对象、类IO实例或单例对象,则会引发TypeError

有些东西的状态(如OS文件描述符或块)无法通过封送处理序列化。您注意到的错误是:

无法使用默认进程转储哈希

因此,模型中的某个人有一个实例变量,它是一个哈希,该哈希使用一个块来提供默认值。
column\u methods\u hash
方法使用这种散列,甚至将散列缓存在
@dynamic\u methods\u hash
中<代码>列\u方法\u散列
将由公共方法(间接)调用,例如
respond\u to?
method\u missing

respond\u to?
method\u missing
中的一个可能迟早会在每个AR模型实例上被调用,调用任何一个方法都会使对象不可分解。因此,AR模型实例在Rails 3中基本上是不可分解的

有趣的是,2.3.8中的
respond_to?
method_missing
实现也有一个使用块作为默认值的哈希支持。2.3.8缓存是如此的幸运,因为有一个后端可以处理整个对象,或者它在对象中有进程散列之前使用了封送;或者,您使用的是缓存后端,这只不过是一个大的散列

在lambdas中使用多个作用域可能最终会在AR对象中存储过程;我希望lambda与类(或单例类)一起存储,而不是与对象一起存储,但我没有进行分析,因为
respond\u to?
method\u missing
的问题使得
范围
问题无关

你能做些什么 我认为你在缓存中存储了错误的东西,而且运气很好。您可以开始正确地使用Rails缓存(即存储简单生成的数据而不是整个模型),也可以实现中概述的
封送转储
/
封送加载
\u转储
/
\u加载
方法。或者,您可以使用一个MemoryStore后端,并将自己限制为每个服务器进程一个不同的缓存


执行摘要 您不能依赖于将ActiveRecord模型对象存储在Rails缓存中,除非您准备自己处理编组,或者希望将自己限制在MemoryStore缓存后端



问题的确切根源在最近版本的Rails中已经发生了变化,但是仍然有许多与哈希相关的
default\u proc
s实例。

多亏了mu,mu的分析太短了。现在,我已通过以下方法成功地使我的模型序列化:

def marshal_dump
  {}.merge(attributes)
end

def marshal_load stuff
  send :initialize, stuff, :without_protection => true
end
我还有一些“虚拟属性”集合b
VIRTUAL_ATTRIBUTES = [:author_name]

attr_accessor *VIRTUAL_ATTRIBUTES

def marshal_dump
  virtual_attributes = Hash[VIRTUAL_ATTRIBUTES.map {|col| [col, self.send(col)] }]
  {}.with_indifferent_access.merge(attributes).merge(virtual_attributes)
end

def marshal_load stuff
  stuff = stuff.with_indifferent_access
  send :initialize, stuff, :without_protection => true
  VIRTUAL_ATTRIBUTES.each do |attribute|
    self.send("#{attribute}=", stuff[attribute])
  end
end
Rails.cache.fetch([self.id, 'relA']) do
  relA.where(
      attr1: 'some_value'
  ).order(
      'attr2 DESC'
  ).includes(
      :rel_1,
      :rel_2
  ).decorate.to_a
end
your_hash.default = nil # clear the default_proc