我遇到了轨道问题.缓存3.1.0上的方法.rc4(ruby 1.9.2p180(2011-02-18修订版30909)[x86_64-darwin10]).代码在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>'

I have tried using the :raw => true option as an alternative, but that isn't working because the Rails.cache.fetch blocks are attempting to cache objects.

有什么建议吗?提前谢谢!

推荐答案

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


Why It Happens

这是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

注意serialized_value的赋值.如果你在cache.rb中翻一翻,你会发现它使用Marshal将对象序列化为字节字符串,然后它们进入缓存,然后再次封送以反序列化对象.压缩问题在这里并不重要,重要的是封送的使用.

问题is that:

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

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

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

因此,模型中的某个人有一个实例变量,它是一个哈希,该哈希使用一个块来提供默认值.column_methods_hash方法使用这样的散列,甚至将散列缓存在@dynamic_methods_hash中;column_methods_hash将通过respond_to?method_missing等公共方法(间接)调用.

respond_to?method_missing中的一个可能迟早会在每个AR模型实例上被调用,调用这两个方法都会使对象不可分解.因此,AR模型实例在Rails 3中基本上是不可变量化的.

有趣的是,2.3.8中的respond_to?method_missing实现也有一个使用块作为默认值的哈希支持.2.3.8缓存是"[...]is meant for caching strings.",所以你很幸运,有一个后端可以处理整个对象,或者在对象中有进程哈希之前,它使用了Marshal;或者,您使用的是MemoryStore缓存后端,这只不过是一个大杂凑.

在lambdas中使用多个作用域可能最终会在AR对象中存储过程;我希望lambda与类(或singleton类)一起存储,而不是与对象一起存储,但我没有进行分析,因为respond_to?method_missing的问题使scope问题变得无关紧要.

What You Can Do About It

我认为你在缓存中存储了错误的东西,而且运气很好.您可以开始正确使用Rails缓存(即存储简单生成的数据,而不是整个模型),也可以实现Marshal中概述的marshal_dump/marshal_load_dump/_load方法.或者,您可以使用MemoryStore后端之一,并将自己限制 for each 服务器进程一个不同的缓存.


Executive Summary

你不能依赖于将ActiveRecord模型对象存储在Rails缓存中,除非你已经准备好自己处理编组,或者你想将自己限制在MemoryStore缓存后端.


问题的确切来源在更新版本的Rails中已经改变,但仍有许多与哈希相关的default_procs实例.

Ruby-on-rails相关问答推荐

Rails 7通过引擎将方法自动加载到控制器中的方法

链轮轨道V4链接到外部Ruby 的 list 文件

错误AESGCMOpen获取密码:消息身份验证失败:Golang解密GCM

Rails:has_many 通过不返回结果

Rails - 如何从模型中查询 has_many

别名可能与不同模型的相同别名有许多关联?

撬Ruby 如何重新加载?

有没有办法在 Rails 3.1 中检测用户代理

禁用冻结字符串文字注释判断

如何使用 capistrano deploy 定位特定的提交 SHA

如何在where子句中组合两个条件?

如何在 rspec 中运行单个测试?

Rails - 获取没有 GET 参数的当前 url

Rails - 从日期和时间戳获取日期

Ruby gem 命名约定

如何测试也定义为辅助方法的 ApplicationController 方法?

ruby on rails 如何处理 NaN

Date.current 和 Date.today 有什么区别?

如何在不触及 updated_at 属性的情况下更新单个属性?

没有UTC的Rails 3默认日期时间格式