使用Proc作为缓存的关键

时间:2013-10-25 17:33:59

标签: ruby-on-rails ruby caching block proc

我正在尝试将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作为缓存

2 个答案:

答案 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上覆盖:==可能是一个坏主意。