Schienen 3.1 mit mehreren Datenbanken verbinden

77

Bei ShowNearby haben wir eine sehr große Migration von PHP zu RoR 3.1 durchgeführt und stehen vor mehreren Problemen, die einige von Ihnen möglicherweise bereits gelöst haben.

Wir haben große Datenmengen und haben beschlossen, unsere Datenbank in mehrere Datenbanken aufzuteilen, die wir separat verarbeiten können. Beispielsweise sind unsere Konten, Orte, Protokolle und andere in mehrere Datenbanken aufgeteilt

Wir brauchen Migrationen, Spielpaarungen, Modelle, um gut zu spielen, und bisher war es ziemlich chaotisch. Einige unserer Anforderungen an eine akzeptable Lösung:

  • Ein Modell sollte sich auf eine Tabelle in einer der Datenbanken beziehen.
  • rake db: drop - sollte die gesamte Datenbank env löschen, die wir in database.yml angegeben haben
  • rake db: create - sollte die gesamte Datenbank env erstellen, die wir in database.yml angeben
  • rake db: migrate - sollte Migrationen zu den verschiedenen Datenbanken ausführen
  • rake db: test - sollte Geräte greifen und in die verschiedenen Datenbanken und Testeinheiten / Funktionen / etc. ablegen

Wir erwägen, für jede Datenbank separate Rails-Projekte festzulegen und diese mit ActiveResource zu verbinden. Wir sind jedoch der Meinung, dass dies nicht sehr effizient ist. Hat sich jemand von Ihnen schon einmal mit einem ähnlichen Problem befasst?

Fer Martin
quelle
Wir erwägen auch ein Upgrade von einer PHP-Anwendung auf eine Rails-Anwendung. Hattest du Glück damit?
Tommyixi
Hi @Tommyixi: Das ist sehr lange her und seitdem hat sich viel geändert. Rückblickend denke ich jetzt, dass es eine bessere Lösung ist, sie in einer Datenbank zusammenzufassen, als sie in mehrere Datenbanken
aufzuteilen

Antworten:

142

Auf die Antwort von Wukerplank können Sie die Verbindungsdetails auch wie gewohnt in database.yml mit einem Namen wie folgt einfügen:

log_database_production:
  adapter: mysql
  host: other_host
  username: logmein
  password: supersecret
  database: logs

Dann in Ihrem speziellen Modell:

class AccessLog < ActiveRecord::Base
  establish_connection "log_database_#{Rails.env}".to_sym
end

Damit diese lästigen Anmeldeinformationen nicht in Ihrem Anwendungscode enthalten sind.

Bearbeiten: Wenn Sie diese Verbindung in mehreren Modellen wiederverwenden möchten, sollten Sie eine neue abstrakte Klasse erstellen und von dieser erben, da Verbindungen eng mit Klassen gekoppelt sind (wie hier , hier und hier erläutert ) und neue Verbindungen für erstellt werden jede Klasse.

Wenn dies der Fall ist, richten Sie die Dinge folgendermaßen ein:

class LogDatabase < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "log_database_#{Rails.env}".to_sym
end

class AccessLog < LogDatabase
end

class CheckoutLog < LogDatabase
end
Unixmonkey
quelle
Wie kann man die Umweltveränderungen berücksichtigen? So möchte ich zum Beispiel in der Entwicklung establish_connectionmit der log_devDatenbank arbeiten, aber in der Produktion möchte ich establish_connectionmit der logDatenbank. Kann ich einfach anrufen Rails.env?
Robert Audi
@ AzizLightestablish_connection "log_database_#{Rails.env}"
Unixmonkey
12
Seien Sie gewarnt. Es scheint, dass bei Verwendung dieser Methode die Verbindungen in den zusätzlichen Datenbanken offen bleiben, ohne sie wiederzuverwenden. Dadurch wird Ihre Anwendung unter starker Last zum Stillstand gebracht.
Altonymous
10
@ Altonymous Guter Punkt. Ich denke, Sie beziehen sich auf dieses Verhalten: github.com/rails/rails/issues/7019 Die Verbindung wird mit der Klasse gekoppelt; Wenn Sie die Verbindung also wiederverwenden müssen, sollten Sie sie in einer abstrakten Klasse einrichten und anstelle von AR :: Base von ihr erben. Ich habe meine Antwort aktualisiert, um dies widerzuspiegeln.
Unixmonkey
Ja. Perfekt. Ich würde mit dieser Antwort antworten, wenn Sie es nicht in Kürze tun würden. : P
Altonymous
18

Das Herstellen einer Verbindung zu verschiedenen Datenbanken ist ganz einfach:

# model in the "default" database from database.yml
class Person < ActiveRecord::Base

  # ... your stuff here

end

# model in a different database
class Place < ActiveRecord::Base

  establish_connection (
    :adapter  => "mysql",
    :host     => "other_host",
    :username => "username",
    :password => "password",
    :database => "other_db"
  )

end

Ich würde mich davor hüten, mehrere Rails-Projekte einzurichten, da Sie dem Datenabruf für Ihre Controller viel Overhead hinzufügen, was die Dinge verlangsamen könnte.

Was Ihre Fragen zu Migrationen, Vorrichtungen, Modellen usw. betrifft: Ich glaube nicht, dass es einen einfachen Weg geben wird. Stellen Sie daher bitte separate Fragen und seien Sie so spezifisch wie möglich.

Eine Konsolidierung der DBs zu einer ist keine Option? Es würde dein Leben viel einfacher machen!

Wukerplank
quelle
1
Das Problem ist, dass das Verbindungspooling mit dem obigen Beispiel nicht ordnungsgemäß verwendet wird
Sam Saffron
11

Unter http://blog.bitmelt.com/2008/10/connecting-to-multiple-database-in-ruby.html wurde ein großartiger Beitrag gefunden, der andere auf den richtigen Weg hinweist

Richten Sie es so ein:

database.yml (Datenbankkonfigurationsdatei)

support_development:
    adapter: blah
    database: blah
    username: blah
    password: blah

support_base.rb (eine Modelldatei)

class SupportBase < ActiveRecord::Base
    self.abstract_class = true #important!
    establish_connection("support_development")
end

tst_test.rb (eine Modelldatei)

class TstTest < SupportBase 
    #SupportBase not ActiveRecord is important!

    self.table_name = 'tst_test'

    def self.get_test_name(id)
        if id = nil
            return ''
        else
            query = "select tst_name from tst_test where tst_id = \'#{id}\'"
            tst = connection.select_all(query) #select_all is important!
            return tst[0].fetch('tst_name')
        end
    end
end

PS, dies deckt Migrationen wirklich nicht ab. Ich glaube nicht, dass Sie Migrationen auf mehr als einer Datenbank mit Rake durchführen können (obwohl ich nicht sicher bin, ob dies schwierig ist, kann es sein, dass dies möglich ist). Dies war nur eine großartige Möglichkeit, andere DBs zu verbinden und abzufragen, die Sie nicht steuern.

TwoByteHero
quelle
5

Möglicherweise möchten Sie auch die Rails-Umgebung anhängen, damit Ihre Entwicklungs- und Testdatenbanken nicht identisch sind.

establish_connection "legacy_#{Rails.env}"
Kris
quelle
3

Im folgenden Artikel wird vorgeschlagen , neue Rake-Aufgaben zu definieren, um Migrationen für mehrere Datenbanken zu erzielen. Jede Aufgabe baut eine eigene Verbindung auf und führt dann die Migration mit dieser Verbindung und dem spezifischen Datenbankordner aus.

Es definiert auch einen Vertrauten db:migrate, der die beiden anderen Aufgaben aufruft.

Einschließlich hier, falls der Link nicht mehr verfügbar ist:

desc "Migrate the database through scripts in db/migrate directory."

namespace :db do
  task :migrate do
    Rake::Task["db:migrate_db1"].invoke
    Rake::Task["db:migrate_db2"].invoke
  end

  task :migrate_db1 do
    ActiveRecord::Base.establish_connection DB1_CONF
    ActiveRecord::Migrator.migrate("db/migrate/db1/")
  end

  task :migrate_db2 do
    ActiveRecord::Base.establish_connection DB2_CONF
    ActiveRecord::Migrator.migrate("db/migrate/db2/")
  end
end

Quelle: Ruby on Rails Stellen Sie eine Verbindung zu mehreren Datenbanken und Migrationen her

cweston
quelle