Frage

Ich habe einen Baum der aktiven Datensatz Objekte, so etwas wie:

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

Es ist zu teuer die complicated_calculation jedes Mal neu zu berechnen. Also, ich brauche einen Weg, um den Wert cachen. Wenn jedoch ein Teil geändert wird, muss er seinen Cache und den Cache der Mutter ungültig zu machen, und Großeltern, etc.

Als Rohentwurf, habe ich eine Spalte die im Cache gespeicherte Berechnung in der „Teilen“ Tabelle zu halten, aber das riecht ein wenig faul. Es scheint, wie es sollte ein sauberer Weg, um die berechneten Werte zwischenzuspeichern, ohne sie an der Seite von den „echten“ Spalten Füllung.

War es hilfreich?

Lösung

  1. Sie können die tatsächlich im Cache gespeicherten Werte in dem Rails-Cache stopfen (Verwendung Memcached, wenn Sie verlangen, dass sie verteilt werden).

  2. Die harte Bit-Cache-Ablauf, aber Cache Ablauf ist ungewöhnlich, nicht wahr? In diesem Fall können wir nur eine Schleife über jede der übergeordneten Objekte wiederum und seinen Cache zappen auch. Ich fügte hinzu, um Ihre Klasse einige Active Magie, um die Eltern zu machen immer Objekte Einfachheit selbst - und Sie brauchen noch nicht einmal Ihre Datenbank zu berühren. Denken Sie daran, rufen Part.sweep_complicated_cache(some_part) gegebenenfalls in Ihrem Code -. Sie können dieses in Rückrufe, etc, aber ich kann es nicht für Sie hinzufügen, weil ich nicht verstehe, wenn complicated_calculation ändert

    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
    

Andere Tipps

Ich schlage vor, Assoziation Rückrufe verwenden.

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

Nice and easy =)

Haben Sie ein Feld ähnlich einem Zähler-Cache. Zum Beispiel: order_items_amount und haben, dass ein im Cache gespeicherte berechnetes Feld sein.

Verwenden Sie einen after_save Filter das Feld auf irgendetwas neu zu berechnen, die diesen Wert ändern können. (Einschließlich der Datensatz selbst)

Edit: Das ist im Grunde, was Sie jetzt haben. Ich weiß nicht, jeder sauberere Lösung, wenn Sie zwischengespeichert berechnete Felder in einer anderen Tabelle speichern wollten.

Sie entweder ein BEFORE_SAVE oder ein Active Observer mit dem Weg zu gehen, die im Cache gespeicherten Wert ist up-to-date, um sicherzustellen. Ich würde eine BEFORE_SAVE verwenden und dann überprüfen, um zu sehen, ob der Wert, den Sie tatsächlich geändert in der Berechnung verwendet werden. Auf diese Weise müssen Sie den Cache nicht aktualisiert werden, wenn Sie nicht brauchen.
Speichern des Werts in der DB können Sie die Berechnungen über mehrere Anfragen zwischenzuspeichern. Eine weitere Möglichkeit hierfür ist der Wert in memcache zu speichern. Sie können für diesen Wert eine spezielle Zugriffs und Setter machen, die die memcache überprüfen und aktualisieren, wenn nötig.
Ein anderer Gedanke: Wird es Fälle, in denen Sie einen Wert in eines der Modelle ändern und müssen die Berechnung aktualisiert werden, bevor Sie die sparen Sie? In diesem Fall müssen Sie schmutzigen Cache-Wert, wenn Sie eine der Berechnungswert in dem Modell aktualisieren, nicht mit einem BEFORE_SAVE.

Ich habe das manchmal gefunden gibt es gute Gründe, um de-normalisieren Informationen in Ihrer Datenbank. Ich habe etwas ähnliches in einer Anwendung, die ich arbeite, und ich nur, dass Feld zu jeder Zeit die Sammlung Änderungen neu berechnen.

Es hat keinen Cache verwenden und speichert die aktuellsten Figur in der Datenbank.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top