当前位置:  开发笔记 > 编程语言 > 正文

如何在rails中缓存计算列?

如何解决《如何在rails中缓存计算列?》经验,为你挑选了2个好方法。

我有一个活动记录对象树,如:

class Part < ActiveRecord::Base
  has_many :sub_parts, :class_name => "Part"

  def complicated_calculation
    if sub_parts.size > 0
      return self.sub_parts.inject(0){ |sum, current| sum + current.complicated_calculation }
    else
      sleep(1)
      return rand(10000)
    end
  end

end

每次重新计算复杂的计算成本太高.所以,我需要一种缓存值的方法.但是,如果更改了任何部分,则需要使其缓存及其父级和祖父级等的缓存无效.

作为一个粗略的草稿,我创建了一个列来保存"部件"表中的缓存计算,但这闻起来有点腐烂.看起来应该有一种更简洁的方法来缓存计算值,而不会将它们填充到"真实"列旁边.



1> August Lille..:

我建议使用关联回调.

class Part < ActiveRecord::Base
  has_many :sub_parts,
    :class_name => "Part",
    :after_add => :count_sub_parts,
    :after_remove => :count_sub_parts

  private

  def count_sub_parts
    update_attribute(:sub_part_count, calculate_sub_part_count)
  end

  def calculate_sub_part_count
    # perform the actual calculation here
  end
end

好又容易=)


我猜这将无法处理从另一个方向(不是通过has_many通过*)创建sub_part的情况,例如:Part.create(:parent_part => the_parent_part)。我可能会在Part上添加一个after_create回调,只是为了确保在这种情况下也触发count_sub_parts ...
只是在Rails 4中做了一些测试并进行了验证,当您直接创建子记录(sub_part)时,这些after_add和after_remove挂钩不会触发

2> Patrick McKe..:

    您可以在Rails缓存中填充实际缓存的值(如果需要将其分发,请使用memcached).

    困难的一点是缓存到期,但缓存到期是不常见的,对吧?在这种情况下,我们可以依次遍历每个父对象并删除其缓存.我在你的类中添加了一些ActiveRecord魔法,以使父对象本身变得简单 - 你甚至不需要触摸你的数据库.记得Part.sweep_complicated_cache(some_part)在你的代码中适当地调用- 你可以将它放在回调等中,但是我不能为你添加它,因为我不知道什么时候complicated_calculation会改变.

    class Part < ActiveRecord::Base
      has_many :sub_parts, :class_name => "Part"
      belongs_to :parent_part, :class_name => "Part", :foreign_key => :part_id
    
      @@MAX_PART_NESTING = 25 #pick any sanity-saving value
    
      def complicated_calculation (...)
        if cache.contains? [id, :complicated_calculation]
          cache[ [id, :complicated_calculation] ]
        else
          cache[ [id, :complicated_calculation] ] = complicated_calculation_helper (...)
        end
      end
    
      def complicated_calculation_helper
        #your implementation goes here
      end
    
      def Part.sweep_complicated_cache(start_part)
        level = 1  # keep track to prevent infinite loop in event there is a cycle in parts
        current_part = self
    
        cache[ [current_part.id, :complicated_calculation] ].delete
        while ( (level <= 1 < @@MAX_PART_NESTING) && (current_part.parent_part)) {
         current_part = current_part.parent_part)
         cache[ [current_part.id, :complicated_calculation] ].delete
        end
      end
    end
    

推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有