views:

104

answers:

1

I've seen this post a few times, but haven't really found the answer to this specific question.

I'd like to run a rails application that based on the detected request.host (imagine I have two subdomains points to the same rails app and server ip address: myapp1.domain.com and myapp2.domain.com).

I'm trying to have myapp1 use the default "production" database, and myapp2 requests always use the alternative remote database. Here is an example of what I tried to do in Application controller that did not work:

class ApplicationController < ActionController::Base
  helper :all 
  before_filter :use_alternate_db

  private

    def use_alternate_db

      if  request.host == 'myapp1.domain.com'
        regular_db
      elsif request.host == 'myapp2.domain.com'
        alternate_db
      end

    end

    def regular_db
      ActiveRecord::Base.establish_connection :production
    end

    def alternate_db
      ActiveRecord::Base.establish_connection(
      :adapter => 'mysql',
      :host => '...',
      :username => '...',
      :password => '...',
      :database => 'alternatedb'
      )

    end
end

The problem is when it switches databases using this method, all connections (including valid sessions across the different subdomains get interrupted...). All examples online have people controlling database connectivity at the model level, but this would involve adding code all over my application. Is there some way to globally switch database connections on a per-request basis in the manner I'm suggesting above WITHOUT having to inject code all over my application?

The added complexity here is I'm using Heroku as a hosting provider, so I have no control at the apache / rails application server level.

I have looked at solutions like dbcharmer and magicmodels, but none seem to show examples of doing it in the manner that I'm trying to. Thanks for any help!

+2  A: 

I'm keying off this line from your question:

all connections (including valid sessions across the different subdomains get interrupted...).

It sounds like there are some models that you'd like to have connecting to the production database, from which I guess you are storing the sessions in the database. It looks like you want the sessions to stay connected to production and the rest you'd like to switch to your other DB.

You can do this by inserting a new class between ActiveRecord and the Models you'd like to change change the connection on.

This class would look like this:

class MyAppModel < ActiveRecord::Base
  self.abstract_class = true
end

then models that need to be change should inherit from it:

class User < MyAppModel
  #stuff
end

Models that shouldn't change should stay connected to ActiveRecord.

Finally, in your ApplicationController, call establish_connection on the MyAppModel instead.

You can do this with as many Classes as you want. The Model classes will work backwards up the heirarchy until they find the first class with a valid connection, and use that to interact with the database. No other changes are necessary.

Tilendor
Thanks Tilendor, this actually worked! I ended up extending ALL of my models (including adding a Session model to represent the database persisted sessions I'm using). Curious where I can learn a little more about how ActiveRecord establishes connections for model instances..
Zaqintosh
The RDoc has some information on this. Go to http://ar.rubyonrails.org/classes/ActiveRecord/Base.html and find the heading named "Connection to multiple databases in different models". Additionally you can look at the docs for the Connection pool too: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ConnectionPool.html
Tilendor
I have found an interesting issue above.. even though database calls are now completely separated (based on the detected hostname), it seems the slower connection of one influences the other. This might cause serious probs in production. I tested by accessing a part of the web app that performs a heavy db operation, then from another browser reloading a page on the "other database" that is normally instantaneous... I can actually see it waiting and waiting for the other database call to complete.Is the entire collection pool eliminated when it switches?
Zaqintosh
How many instances do you have running? If you have just one Mongrel, it can only handle a single request at a time. If you have multiple mongrels and load balance them, apache(or nginx) can be configured to not make a request to that specific mongrel until its free again. If you changed back to referencing the same DB, and had two browsers, made the slow request, and then the fast one you would experience the same thing.
Tilendor