Mehrere simultane Datenbankverbindungen mit ActiveRecord
Die Aufgabe klang trivial:
Mehrere Server sammeln Daten und speichern diese in einer lokalen Datenbank. Es ist eine Rails Anwendung zu schreiben, die die Zustände aller Datenbanken auf einer Website darstellt.
Lösung
Die Datei database.yml
enthält für jeden Betriebsmodus einer Rails-Anwendung die passende Konfiguration einer Datenbankverbindung, welche von ActiveRecord automatisch benutzt wird, wenn keine andere angegeben wurde.
Wir versuchten als erstes die jeweilige Verbindung für einen Zugriff anzugeben. Dabei stellte sich heraus, dass ActiveRecord für jedes Modell nur eine Datenbankverbindung erlaubt. Intern wird eine statische HashMap von Klassennamen auf eine Datenbankverbindung gehalten. Diese wird automatisch für alle Anfragen für das Modell genutzt.
Im Umkehrschluss bedeutet das, dass zur Lösung der Aufgabe eine Modell-Klasse je Server benötigt wird. Zum Glück ist Ruby eine dynamische Sprache. Wir generieren uns einfach zur Laufzeit eine passende Ableitung für eine Datenbankverbindung:
module ConnectionModelFactory
def self.build(connection, model)
symbol = ([model.to_s, connection.camelize.to_s] * '_').to_sym
unless self.const_defined? symbol
derived = Class.new model
self.const_set(symbol, derived)
end
self.const_get symbol
end
end
Mit der neuen Klasse können wir nun die Datenbankverbindung einfach festlegen:
derived.establish_connection connection
Soweit so gut! Das Ganze funktioniert nur so lange, wie das Modell keine externen Relationen benötigt. Damit diese ebenfalls über die Datenbankverbindung abgefragt werden können, erzeugen wir alle Relationen mit angepassten Modellen neu:
derived.reflect_on_all_associations.each do |a|
connection_a = ConnectionModelFactory.build(connection, a.klass)
a_options = a.options.merge(:class_name => connection_a.name)
derived.create_reflection(a.macro, a.name, a_options, a.active_record)
end
Damit ist die Aufgabe gelöst.
Als Erweiterung könnte man nun noch alle Anfragen zu den Servern in eigenen Threads durchführen.