Context
I recently found myself in a situation where I needed to have my Rails application connected to two different databases.
I’m not going to dive into in the reasons for this, but I’d like to show the way I found to do it, and the problems (just a few) along the way.
ActiveRecord::Base
Rails loads the database configuration from the config/database.yml file, and saves it into ActiveRecord::Base#configurations for the later use with ActiveRecord::Base#establish_connection.
Once the connection to the database is established it is reused, however, if the connection to the database is lost at some point, ActiveRecord will try to reconnect with the configuration it has stored.
Second Connection
For the second database connection we’ll create another “Base” class to inherit from which will inherit from ActiveRecord::Base but will override the configuration to connect to the other database, let’s say:
module Backend
class Model < ActiveRecord::Base
@@conf = {}
@abstract_class = true
def self.configurations
@@conf
end
def self.configurations=(configurations = {})
raise RuntimeError.new('Invalid configuration Format') unless configurations.is_a? Hash @@conf = configurations
end
end
end
This class will load it’s configuration from config/backend_database.yml (which is a file with the exact same format than config/database.yml but using different db access configuration). For this, create an initializer (config/initializers/backend_database.rb) with the following lines:
require 'erb' @@backend_configurations ||= YAML::load(ERB.new(IO.read(File.join(Rails.root, "config", "backend_database.yml"))).result) Backend::Model.configurations = @@backend_configurations Backend::Model.establish_connection
Why override the configurations method?
If you use the inherited configurations method it will overwrite the @@configurations variable and if ActiveRecord::Base need to restore a lost connection it will attempt to connect to the second database and will turn your application into a mess.
Also, note we are not using the @@configuration class variable either, for the same reason.
Last but not least, the @abstract_class = true is necessary for a correct table names guessing.
And that’s about it, you can now have your models inherit from ActiveRecord::Base to have the databases configured on config/database.yml as their data source and inherit from Backend::Model to have the databases configured on config/backend_database.yml and use ActiveRecord relationships completely transparent, for example, you could have:
class Post < ActiveRecord::Base has_many :comments end class Comment < Backend::Model belongs_to :post end
Warning
Rake tasks like db:migrate won’t take this second configuration into consideration, so this approach won’t cover migrations.
It should, at least theoretically (because I haven’t tried it yet), behave correctly with the test environment.
And that’s it. I hope it helps.
@k4nd4lf

