she*_*edd 40 ruby memcached caching ruby-on-rails ruby-on-rails-3
我在3.1.0.rc4上遇到了Rails.cache方法的问题(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)上的相同应用程序中正常工作,但在升级后开始返回错误.我还没弄清楚原因.
尝试缓存具有多个范围的对象时,似乎会发生错误.
此外,无论使用多少范围,使用lambdas的任何范围都会失败.
我从这些模式中遇到了失败:
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
Run Code Online (Sandbox Code Playgroud)
这是我收到的错误:
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>'
Run Code Online (Sandbox Code Playgroud)
我已经尝试使用:raw => true选项作为替代,但这不起作用,因为Rails.cache.fetch块正在尝试缓存对象.
有什么建议?提前致谢!
mu *_*ort 100
这可能有点冗长,但我不得不花一些时间使用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
Run Code Online (Sandbox Code Playgroud)
请注意分配给serialized_value
.如果你在里面徘徊cache.rb
,你会发现它使用Marshal在进入缓存之前将对象序列化为字节字符串,然后再使用Marshal来反序列化对象.压缩问题在这里并不重要,重要的是使用Marshal.
问题是:
无法转储某些对象:如果要转储的对象包括绑定,过程或方法对象,类IO实例或单例对象,则会引发TypeError.
有些东西具有Marshal无法序列化的状态(例如OS文件描述符或块).您注意到的错误是:
无法使用默认proc转储哈希
因此,模型中的某个人拥有一个Hash实例变量,而Hash使用一个块来提供默认值.该column_methods_hash
方法使用这样的哈希,甚至在内部缓存哈希@dynamic_methods_hash
; column_methods_hash
将通过respond_to?
和等公共方法(间接)调用method_missing
.
迟早会调用每个AR模型实例之一respond_to?
或者method_missing
很可能会调用其中一个方法,并且调用任一方法都会使您的对象不可序列化.因此,AR模型实例在Rails 3中基本上是不可序列化的.
有趣的是,2.3.8中的respond_to?
和method_missing
实现也由一个使用块作为默认值的Hash支持.2.3.8缓存是"[...]用于缓存字符串." 所以你很幸运得到了一个可以处理整个物体的后端,或者在你的物体中有一个带有散列的触发器之前它使用了Marshal; 或者你可能正在使用MemoryStore
缓存后端,这只不过是一个大哈希.
使用多个scope-with-lambdas可能最终将Procs存储在AR对象中; 我期望的lambda表达式存储与类(或者单例类),而不是对象,但我没有与分析的问题打扰respond_to?
和method_missing
使scope
问题无关.
我认为你已经把错误的东西存放在你的缓存中并且很幸运.您可以正确地开始使用Rails缓存(即存储简单生成的数据而不是整个模型),或者您可以实现Marshal中概述的marshal_dump
/ marshal_load
或_dump
/ _load
方法.或者,您可以使用其中一个MemoryStore后端,并将自己限制为每个服务器进程一个不同的缓存.
您不能依赖于在Rails缓存中存储ActiveRecord模型对象,除非您已准备好自己处理编组,或者您希望将自己限制在MemoryStore缓存后端.
多亏了他的出色分析.我已经设法让我的模型现在用这个序列化:
def marshal_dump
{}.merge(attributes)
end
def marshal_load stuff
send :initialize, stuff, :without_protection => true
end
Run Code Online (Sandbox Code Playgroud)
我还使用AS
例如直接SQL连接查询设置了一些"虚拟属性" SELECT DISTINCT posts.*, name from authors AS author_name FROM posts INNER JOIN authors ON author.post_id = posts.id WHERE posts.id = 123
.为了使这些工作,我需要attr_accessor
为每个声明一个,然后转储/加载它们,如下所示:
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
Run Code Online (Sandbox Code Playgroud)
使用Rails 3.2.18
归档时间: |
|
查看次数: |
16924 次 |
最近记录: |