【问题标题】:Use Proc as a key for caching使用 Proc 作为缓存的键
【发布时间】:2013-10-25 17:33:58
【问题描述】:

我正在尝试将 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>

因此,即使对于相同的请求,我也会遇到缓存未命中。

如果 proc 在代码和变量值方面相同,我如何使用缓存键中的 block/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 中实现这种缓存的最佳方法是什么?

附注:

使用 Rails4 和 reddis 作为缓存

【问题讨论】:

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


    【解决方案1】:

    每次你用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。

    【讨论】:

      【解决方案2】:

      您可能会通过这种方式获得一些里程。此函数返回一个简单的 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 上覆盖 :== 可能是个坏主意。

      【讨论】:

        猜你喜欢
        • 2015-09-19
        • 1970-01-01
        • 1970-01-01
        • 2015-04-17
        • 2014-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多