简体   繁体   中英

Rails 4.2: How to dynamically change the connection used by a thread?

How can I change the connection of one thread? I want to do that:

# 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 }

I want that these 2 threads (the main and the one what I created) runs asynchronously but I discover that Rails uses one single connection to do this. So, all my efforts to run 2 queries at the same time and gain time, are ruined - Rails tapers my requests in 1 single and synchronized pool. I had tried the below too, but although Rails create a new connection, ActiveRecord (the "User") doesn't use it:

# 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

I had tried to set "ActiveRecord::Base.connection = conn" but didn't work too.

It is possible to each thread have your own connection? How can I set the thread connection?

As per rails guide , you have to wrap your application code in an executor block.

Try this(for Rails version: 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 }

This should work assuming your pool size value in config/database.yml is more than 1.

Ok, I got it. But I had to make a little hack on "retrieve_connection" to achieve this. I put this on my 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

I just modify the "conn = pool.connection" to "conn = Thread.current["connection"] || pool.connection". This allows me to define the connection using a custom thread variable. With this hack, my code looked like this:

# 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 }

or even better, create a new Thread for each update:

# 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: this meets my needs, but I wonder if there isn't a native method to change the connection of a thread. The way I've done was so ugly :/ So, if you know a better way, please share it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM