Rails 4.2:如何动态更改线程使用的连接?

时间:2015-01-25 22:33:43

标签: ruby-on-rails ruby multithreading activerecord database-connection

如何更改一个线程的连接?我想这样做:

# slow query process at background
Thread.new do
  User.all { |user| user.update_attributes(some_field: (rand * 100).to_i) }
end

# more slow query process
100000.times { User.where(some_field_2: (rand * 100).to_i).first }

我希望这两个线程(主要和我创建的一个)以异步方式运行,但我发现Rails使用一个连接来执行此操作。因此,我在同一时间运行2个查询并获得时间的所有努力都被破坏了--Rails在单个和同步池中逐渐减少了我的请求。我也试过了下面的内容,但是虽然Rails创建了一个新连接,但ActiveRecord(" User")并没有使用它:

# slow query process at background
Thread.new do
  conn = ActiveRecord::Base.connection_pool.checkout()
  User.all {|user| user.update_attributes(some_field: (rand * 100).to_i) }
  ActiveRecord::Base.connection_pool.checkin(conn)
end

我曾试图设置" ActiveRecord :: Base.connection = conn"但也没有用。

每个线程都可以拥有自己的连接吗?如何设置线程连接?

2 个答案:

答案 0 :(得分:0)

根据rails guide,您必须将应用程序代码包装在执行程序块中。

尝试一下(对于Rails版本:4):

Thread.new do
  ActiveRecord::Base.connection_pool.with_connection do
    User.all { |user| user.update_attributes(some_field: (rand * 100).to_i) }
  end
end

# more slow query process
100000.times { User.where(some_field_2: (rand * 100).to_i).first }

如果config/database.yml中的池大小值大于1,这应该可以工作。

答案 1 :(得分:-1)

好的,我明白了。但是我不得不在“retrieve_connection”上做一点破解来实现这个目标。我把它放在我的application.rb上:

module ActiveRecord
  module ConnectionAdapters
    class ConnectionHandler
      def retrieve_connection(klass) #:nodoc:
        pool = retrieve_connection_pool(klass)
        raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
        conn = Thread.current["connection"] || pool.connection
        raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
        conn
      end
    end
  end
end

我只是将“conn = pool.connection”修改为“conn = Thread.current [”connection“] || pool.connection”。这允许我使用自定义线程变量定义连接。有了这个黑客,我的代码看起来像这样:

# slow query process at background
Thread.new do
  Thread.current["connection"] = ActiveRecord::Base.connection_pool.checkout()
  User.all {|user| user.update_attributes(some_field: (rand * 100).to_i) }
  ActiveRecord::Base.connection_pool.checkin(Thread.current["connection"])    
end

# more slow query process
100000.times { User.where(some_field_2: (rand * 100).to_i).first }

甚至更好,为每次更新创建一个新的线程:

# slow query process at background
User.all do |user|
  Thread.new do
    Thread.current["connection"] = ActiveRecord::Base.connection_pool.checkout()
    user.update_attributes(some_field: (rand * 100).to_i)
    ActiveRecord::Base.connection_pool.checkin(Thread.current["connection"])
  end
end

# more slow query process
100000.times { User.where(some_field_2: (rand * 100).to_i).first }

PS: 这符合我的需求,但我想知道是否没有本地方法来更改线程的连接。我做的方式太难了:/所以,如果你知道更好的方法,请分享。