Domanda

I'm experimenting to understand a problem in production, so I've thrown this snippet inside a controller action in dev to test:

start = Time.now
num_threads = 6
results = Queue.new
saved_results = []
threads = []
connections = []
semaphore = Mutex.new

# start threads
(1..num_threads).each do |i|
  threads << Thread.new do
    #semaphore.synchronize { connections << ActiveRecord::Base.connection } # for cleanup?

    #ActiveRecord::Base.connection.execute("select sleep(1.6);")   # runs sequentially
    sleep(1.6)                                                    # runs concurrently
    result = User.find_by_id(i)
    results << [i, result]
  end
end

# end option 1 - let everyone finish
threads.each(&:join)

# end option 2 - simulate early exit condition
#while saved_results.count < 3 do saved_results << results.pop end
#threads.each(&:exit)

# cleanup/close open connections?
#connections.select(&:active?).each(&:disconnect!)

elapsed = Time.now - start
render :text => [ elapsed.to_s, saved_results.size, results.size ].join(", ")

sleep(1.6) executes in approximately 1.6 seconds, as expected.

However, the ActiveRecord select sleep(1.6); takes 6 * 1.6 = 9.6 seconds, despite mysql console show processlist; displaying that independent connections are opened for each thread*.

What's going on? Why won't the ActiveRecord queries run concurrently? I've also experienced this in production console.

I do have config.threadsafe! set in config/environment.rb. If it matters, I'm using Rails 2.3.

*These connections have to be manually closed? Production always has a lot of open connections that are doing nothing, causing Mysql::Error: Too many connections. I'll probably submit this issue as a another question.

È stato utile?

Soluzione

Some remarks:

  • rails 2.3 itself is afaik itself not really threadsafe, since rails 3.x it is. But for this case that does not really matter I think.
  • you should be using ruby 1.9 at least. The "green threads" in 1.8 are less than optimal. While treading in ruby 1.9 still is not optimal, it is better. For real threading you should check out jruby or rubinius (no GIL).
  • you should be using the mysql2 gem. The mysql gem keeps the GIL while waiting for a response from the database.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top