views:

179

answers:

2

I'm developing a rails application in which each subdomain has a separate database. And I'm doing something like this.

#app/controller/application_controller.rb
class ApplicationController < ActionController::Base
  before_filter :select_database

  private
  def select_database
    MyModel.use_database(request.subdomains.first)
  end
end

#app/model/my_model.rb
class MyModel < ActiveRecord::Base

  def self.use_database path
    establish_connection :adapter => 'sqlite3', :database =>
      File.join(RAILS_ROOT, "db", "sqlite3", path)
  end
end

Now, in production mode, requests come and execute in this order.

  1. Request "A" comes for subdomain a.example.net and MyModel establishes a connection with database "a".
  2. Now another request "B" comes for subdomain b.example.net and MyModel establishes a connection with database "b".
  3. Now if request "A" tries to do a MyModel.find_*() which database would it access? "a" or "b"?

I believe that this is what we call "thread safety" or "race condition", and if this is so, then how can we avoid it while implementing an application using one database per subdomain?

In the above question, my assumption is that executing two requests simultaneously is a normal behaviour for production servers. Or is there some better approach. I don't have much experience of production servers, so please advice.

A: 

It seems like you are using Replications... However I do suggest that you watch the following casts from newrelic to get a better idea on DB scaling:

Scaling Your Database - Part 1

Scaling Your Database - Part 2

khelll
A: 

If there are two models and two databases, why not subclass your MyModel for each database?

class MyModelDomainA < MyModel
  DBA_PATH = "first/db/path"
  def self.use_database
    establish_connection :adapter => 'sqlite3', :database => File.join(RAILS_ROOT, "db", "sqlite3", DBA_PATH)
  end
end
# then in controller:
def select_database
  # or whatever string-manipulation you need to do... 
  # even have a lookup hash to get the correct model?
  model_klass = "MyModel#{request.subdomains.first.capitalize}"
  model_klass.constantize.use_database
end

etc. Obviously this only works if you have a small, fixed number of domain/database pairs. But if so - it means that anybody coming into DomainA will always access the database for Domain A = no race conditions.

Generally, I find that unless you're hacking kernel code, the solution to a race condition is not thread-safety... but to re-think the problem.

Taryn East
For one database per sub-domain, let's assume number of sub-domains is dynamic.
Vikrant Chaudhary
ok, in which case, instead of being a class method (that will update the whole class), could you make it an instance method ie only related to the actual instance you've got in your hand right now? It'll be slower to have to reconnect each time... but it'd be workable.
Taryn East
Alternatively, you can also run the above code on the fly... the beauty of meta-programming means that you can actually make any number of "MyModelDomainA" style classes... at run-time.
Taryn East