如何避免Ruby on Rails应用程序中的竞争条件?

时间:2009-12-17 10:41:37

标签: ruby-on-rails race-condition

我正在开发一个Rails应用程序,其中每个子域都有一个单独的数据库。而我正在做这样的事情。

#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

现在,在production模式下,请求按此顺序进行并执行。

  1. 请求“A”代表子域 a.example.net MyModel与数据库“a”建立连接。
  2. 现在,另一个请求“B”来自子域 b.example.net MyModel与数据库“b”建立连接。
  3. 现在,如果请求“A”尝试执行MyModel.find_*()哪个数据库可以访问? “a”或“b”?
  4. 我认为这就是我们所谓的“线程安全”或“竞争条件”,如果是这样,那么在每个子域使用一个数据库实现应用程序时,我们如何避免它呢?

    在上面的问题中,我的假设是同时执行两个请求是生产服务器的正常行为。或者是否有更好的方法。我对生产服务器的经验不多,所以请提供建议。

2 个答案:

答案 0 :(得分:0)

好像你正在使用复制...但是我确实建议你从newrelic观看以下演员,以便更好地了解数据库缩放:

Scaling Your Database - Part 1

Scaling Your Database - Part 2

答案 1 :(得分:0)

如果有两个模型和两个数据库, 为什么不为每个数据库创建MyModel的子类?

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

等。 显然,这仅适用于少量固定数量的域/数据库对。 但如果是这样的话 - 这意味着任何进入DomainA的人都将始终访问域A =没有竞争条件的数据库。

通常情况下,我发现除非您正在攻击内核代码,否则竞争条件的解决方案不是线程安全的......而是重新考虑问题。