Read my latest article: Was away on vacation (posted Sun, 11 May 2008 22:33:00 GMT)

Multiple Database Connections in Ruby on Rails 15

Posted by Robby Russell Fri, 05 Oct 2007 22:54:00 GMT

We have a client that already has some database replication going on in their deployment and needed to have most of their Ruby on Rails application pull from slave servers, but the few writes would go to the master, which would then end up in their slaves.

So, I was able to quickly extend ActiveRecord with just two methods to achieve this. Anyhow, earlier today, someone in #caboose asked if there was any solutions to this and it prompted me to finally package this up into a quick and dirty Rails plugin.

Introducing… Active Delegate!

To install, do the following:


cd vendor/plugins;
piston import http://svn.planetargon.org/rails/plugins/active_delegate

Next, you’ll need to create another database entry in your database.yml.


login: &login
  adapter: postgresql
  host: localhost
  port: 5432

development:
  database: rubyurl_development
  <<: *login

test:
  database: rubyurl_test
  <<: *login

production:
  database: rubyurl_servant
  <<: *login

# NOTICE THE NEXT ENTRY/KEY
master_database:
  database: rubyurl_master
  <<: *login

At this point, your Rails application won’t talk to the master_database, because nothing is being told to connect to it. So, the current solution with Active Delegate is to create an ActiveRecord model that will act as a connection handler.


  # app/models/master_database.rb
  class MasterDatabase < ActiveRecord::Base
    handles_connection_for :master_database # <-- this matches the key from our database.yml
  end  

Now, in the model(s) that we’ll want to have talk to this database, we’ll do add the following.


  # app/models/animal.rb
  class Animal < ActiveRecord::Base
     delegates_connection_to :master_database, :on => [:create, :save, :destroy]
  end

Now, when your application performs a create, save, or destroy, it’ll talk to the master database and your find calls will retrieve data from your servant database.

It’s late on a Friday afternoon and I felt compelled to toss this up for everyone. I think that this could be improved quite a bit, but it’s working great for the original problem that needed to be solved.

If you have feedback and/or bugs, please send us tickets.

Subscribe to my RSS feed Enjoying the content? Be sure to subscribe to my RSS feed.
Comments

Leave a response

  1. Avatar
    Garry Fri, 05 Oct 2007 23:02:48 GMT

    Aah, pretty neat!

  2. Avatar
    Warren Noronha Fri, 05 Oct 2007 23:04:33 GMT

    Awesome plugin, just what I was looking for ;)

  3. Avatar
    Jereme Fri, 05 Oct 2007 23:29:09 GMT

    A cursory glance at the code shows me that this will fail to handle collections properly. Setting a collection to an explicit list will cause a delete_all to be run. Deletes don’t have callbacks and therefor you will wind up hitting the slave. I have filed a bug.

  4. Avatar
    Joe Van Dyk Sat, 06 Oct 2007 01:50:23 GMT

    Why wouldn’t you want this turned on for all your models? Is it possible to use delegates_connection_to for all of them?

  5. Avatar
    Frank Sat, 06 Oct 2007 04:02:14 GMT

    Can you explain the “&” and ”<<” stuff? I think I’ve seen this before but have never been clear as to how it works.

  6. Avatar
    Frank Sat, 06 Oct 2007 04:04:22 GMT

    Can you explain the “&” and ”<<” stuff? I think I’ve seen this before but have never been clear as to how it works.

  7. Avatar
    Nira Amartha Sat, 06 Oct 2007 07:43:56 GMT

    I think you should use replication features provided by database rather than create one on your own. It will be safer and provide higher performance.

  8. Avatar
    Obsession Sat, 06 Oct 2007 07:46:41 GMT

    Not everything can be done by standard replication features. Thanks for the tip Robby!

  9. Avatar
    Jeff Dean Sat, 06 Oct 2007 18:58:07 GMT
  10. Avatar
    Chuck Vose Sat, 06 Oct 2007 19:43:35 GMT

    @Frank

    doing something like login: &login adaptor: mysql etc: etc

    then development: database: hat_development <<: *login

    So we use & to store a bunch of yaml, and <<: * to put it back in place. I’m not sure the actual code behind it - whether it’s an actual block or if it’s just yaml - but either way, that’s what it’s for.

    Think it originally showed up on Redhanded?

  11. Avatar
    Anonymous Coward Sun, 07 Oct 2007 09:43:35 GMT

    Congratulations to writing your first blog post without “PLANET ARGON” in all caps in the body. That line always turns me off enough that I never read beyond it. This time I could read the whole post, and it was interesting. Thanks!

  12. Avatar
    Anonymous Fri, 12 Oct 2007 18:45:09 GMT

    @Jeff: I don’t think it does any more than Dr. Nic’s magic multi connections, but sounds like it has more issues. However, if you are really trying to handle replicated databases seriously I quite like the approach taken in MySQL Replication Adapter project (http://rubyforge.org/projects/mysql-replicate/). It feels much more complete than doing noob AR method extensions to attempt something much larger.

  13. Avatar
    Rene A. Wed, 24 Oct 2007 20:06:57 GMT

    Superb plugin! I had to change mysqldump to mysqldump5 and added the support to only dump the structure of the database “mysql-style” not schema.rb:

    desc "Dump only them schema and to an SQL file (/db/schema_YYYY_MM_DD.sql)" 
    task :structure => :environment do
      current_date = Time.now.strftime("%Y_%m_%d")
      archive = "#{RAILS_ROOT}/db/schema_#{current_date}.sql" 
      database, user, password = retrieve_db_info
    end
    cmd = "/usr/bin/env mysqldump --opt --skip-add-locks --no-data -u #{user} " 
    puts cmd + "... [password filtered]" 
    cmd += " -p'#{password}' " unless password.nil?
    cmd += " #{database} > #{archive}" 
    result = system(cmd)
  14. Avatar
    Saket Fri, 29 Feb 2008 13:13:11 GMT

    Is this sufficient to connect two databases in one rails app? Could you please explain ? I am experiencing the same issue so need your help?

    Thanks Saket

  15. Avatar
    Saket Fri, 29 Feb 2008 13:13:40 GMT

    Is this sufficient to connect two databases in one rails app? Could you please explain ? I am experiencing the same issue so need your help?

    Thanks Saket

Share your thoughts... (really...I want to hear them)

Comments