我正在尝试将rails请求缓存到一个带有proc的外部服务。
为了能够区分2个请求,重要的是proc是Rails缓存键的一部分。
但是,具有相同proc和proc代码中的值的多个请求仍然会每次评估到不同的proc对象。
例如:
#<Proc:0x007ff6f675dd08@/Users/app/development/work/runner.rb:10>
#<Proc:0x007ff6ffb50790@/Users/app/development/work/runner.rb:10>
因此,即使对于相同的请求,我也会得到缓存未命中。
如果procs在代码和变量值方面相同,我如何在缓存键中使用块/ proc来计算相同的值。
服务电话类似于
def some_method
call_service do
a= 3;
b=6
end
end
def call_service(block)
Rails.cache.fetch(Date.today, block) {
external_service(&block)
}
end
I want to be able to compare blocks :
eg
{a=3, a*a} == {a=3, a*a} => True
{a=3, a*a} == {a=4, a*a} => false
{a=3, a*a} == {a=4, a*a*a} => False
我试过用,
block.source
RubyVM::InstructionSequence.of(block).disasm
但它们都没有捕获块的状态,即变量的值(a = 3等)
在rails中实现此类缓存的最佳方法是什么?
P.S:
使用Rails4和reddis作为缓存
答案 0 :(得分:0)
每次使用do ... end
定义内嵌块时,您将获得不同的 Proc对象。你必须不遗余力地绝对肯定你正在发送完全相同的Proc:
def some_method
@my_call ||= lambda {
a = 3
b = 6
}
call_service(&@my_call)
end
从一开始这可能是一个坏主意。你最好做的是传递一个缓存键:
call_service("3/6") do
# ...
end
然后你缓存这个一致的密钥,而不是任意的Proc。
答案 1 :(得分:0)
你可能会以这种方式获得一些里程。此函数返回一个简单的Proc对象。该对象将具有源位置和绑定,该绑定包含构建Proc时参数a,b和c的状态。
def build_proc(a, b, c)
Proc.new { puts "I was built with params #{a} #{b} #{c}!" }
end
所以我们可以构建我们的Proc对象 -
2.0.0-p195 :121 > p1 = build_proc(1, 2, 3)
=> #<Proc:0x98849e8@(irb):118>
2.0.0-p195 :122 > p2 = build_proc(2, 4, 6)
=> #<Proc:0x985e57c@(irb):118>
要获取参数的状态,可以针对Proc对象调用binding.eval('argument')
。这在Proc的绑定上下文中运行代码。所以你可以说 -
p1.binding.eval('[a, b, c]') == p2.binding.eval('[a, b, c]')
2.0.0-p195 :127 > p1.binding.eval('[a, b, c]') == p2.binding.eval('[a, b, c]')
=> false
2.0.0-p195 :128 > p2 = build_proc(1, 2, 3)
=> #<Proc:0x97ae4b0@(irb):118>
2.0.0-p195 :129 > p1.binding.eval('[a, b, c]') == p2.binding.eval('[a, b, c]')
=> true
显然你可以比较源位置 -
p1.source_location == p2.source_location &&
p1.binding.eval('[a, b, c]') == p2.binding.eval('[a, b, c]')
您需要一种以通用方式从绑定中提取参数的方法,并且您希望组合它们的哈希值,而不是构建一个数组进行比较(例如'a.hash ^ b.hash ^ c.hash'
)。
这确实感觉很糟糕!至少,在Procs上覆盖:==
可能是一个坏主意。