문제

활성 레코드 객체의 나무가 있습니다.

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

매번 복잡한 _calculation을 다시 계산하는 데 너무 많은 비용이 듭니다. 따라서 값을 캐시하는 방법이 필요합니다. 그러나 일부 부품이 변경되면 캐시와 부모의 캐시와 조부모 등의 캐시를 무효화해야합니다.

거친 초안으로, 나는 "부품"테이블에 캐시 된 계산을 고정하기 위해 열을 만들었지 만 이것은 약간 썩은 냄새가납니다. "실제"열에 넣지 않고 계산 된 값을 캐시하는 더 깨끗한 방법이 있어야합니다.

도움이 되었습니까?

해결책

  1. Rails 캐시에 실제로 캐시 된 값을 채울 수 있습니다 (분포가 필요한 경우 Memcached를 사용).

  2. 힘든 비트는 캐시가 만료되지만 캐시 만료는 드문 일입니다. 이 경우 각 상위 객체를 차례로 고리하고 캐시도 Zap 할 수 있습니다. 학부모 객체를 단순화하기 위해 ActiveRecord Magic을 수업에 추가했습니다. 데이터베이스를 만질 필요조차 없습니다. 전화하는 것을 잊지 마십시오 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
    

다른 팁

나는 협회 콜백을 사용하는 것이 좋습니다.

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

멋지고 쉬운 =)

카운터 캐시와 유사한 필드가 있습니다. 예를 들어 : Order_items_amount이며 캐시 된 계산 필드입니다.

After_save 필터를 사용하여 해당 값을 수정할 수있는 모든 것에 대해 필드를 다시 계산하십시오. (레코드 자체 포함)

편집 : 이것은 기본적으로 당신이 지금 가지고있는 것입니다. 캐시 된 계산 필드를 다른 테이블에 저장하지 않는 한 깨끗한 솔루션을 모릅니다.

prever_save 또는 activerecord observer를 사용하면 캐시 된 값이 최신 상태인지 확인하는 방법입니다. 나는 prever_save를 사용한 다음 계산에 사용하는 값이 실제로 변경되었는지 확인합니다. 그렇게하면 필요하지 않은 경우 캐시를 업데이트 할 필요가 없습니다.
DB에 값을 저장하면 여러 요청을 통해 계산을 캐시 할 수 있습니다. 이를위한 또 다른 옵션은 값을 Memcache에 저장하는 것입니다. Memcache를 확인하고 필요한 경우 업데이트 할 수있는 해당 값에 대한 특별 액세서 및 세터를 만들 수 있습니다.
또 다른 생각 : 모델 중 하나에서 값을 변경하고 저장을하기 전에 계산을 업데이트 해야하는 경우가 있습니까? 이 경우 Prever_Save가 아닌 모델의 계산 값을 업데이트 할 때마다 캐시 값을 더럽 힐 필요가 있습니다.

때로는 데이터베이스에서 정보를 제거할만한 이유가 있음을 알았습니다. 나는 작업중인 앱에서 비슷한 것을 가지고 있으며 컬렉션이 변경 될 때마다 해당 필드를 다시 계산합니다.

캐시를 사용하지 않으며 데이터베이스에서 최신 수치를 가장 많이 저장합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top