Wie kann ich eine berechnete Spalte in Schienen-Cache?
-
05-07-2019 - |
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.
Lösung
-
Sie können die tatsächlich im Cache gespeicherten Werte in dem Rails-Cache stopfen (Verwendung Memcached, wenn Sie verlangen, dass sie verteilt werden).
-
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, wenncomplicated_calculation
ändertclass 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.