Techioz Blog

Ruby では、スレッド間でデータベース接続を共有することは可能ですか?

概要

私は 80,000 件ほどのレコードを書き込む小さな Ruby スクリプトを持っています。 各レコードに必要なプロセッサとメモリの負荷はスマーフ ボールよりも小さいですが、それでもすべてのレコードを歩き回るには約 8 分かかります。

スレッドを使用しようと思ったのですが、試してみるとデータベースの接続が不足してしまいました。確かに、それは 200 回接続を試みたときのことであり、実際にはそれよりもうまく制限できました。しかし、このコードを Heroku にプッシュするとき (すべてのワーカーが共有する接続が 20 個あります)、このプロセスが増加したため、他のプロセスをブロックする可能性があります。

コードをリファクタリングしてすべての SQL を結合することを考えましたが、それは本当に非常に面倒に感じられます。

そこで、スレッド間で接続を共有できるようにするためのトリックはあるのでしょうか?処理中に接続変数が変更されるとは予想していなかったので、スレッドフォークが新しい DB 接続を作成する必要があることに実際にはちょっと驚きました。

まあ、どんな助けでもとても素晴らしいでしょう(私と同じように)..ありがとう

START_TIME = Time.now

require 'rubygems'
require 'erb'
require "active_record"

@environment = 'development'
@dbconfig = YAML.load(ERB.new(File.read('config/database.yml')).result)
ActiveRecord::Base.establish_connection @dbconfig[@environment]

class Product < ActiveRecord::Base; end

ids = Product.pluck(:id)
p "after pluck #{Time.now.to_f - START_TIME.to_f}"

threads = [];
ids.each do |id|
  threads << Thread.new {Product.where(:id => id).update_all(:product_status_id => 99); }
  if(threads.size > 4)
    threads.each(&:join)
    threads = [] 
    p "after thread join #{Time.now.to_f - START_TIME.to_f}"
  end
end

p "#{Time.now.to_f - START_TIME.to_f}"

出力

"after pluck 0.6663269996643066"
DEPRECATION WARNING: Database connections will not be closed automatically, please close your
database connection at the end of the thread by calling `close` on your
connection.  For example: ActiveRecord::Base.connection.close
. (called from mon_synchronize at /Users/davidrawk/.rvm/rubies/ruby-1.9.3-p448/lib/ruby/1.9.1/monitor.rb:211)
.....
"after thread join 5.7263710498809814"   #THIS HAPPENS AFTER THE FIRST JOIN.
.....
"after thread join 10.743254899978638"   #THIS HAPPENS AFTER THE SECOND JOIN

解決策

この gem https://github.com/mperham/connection_pool を参照してください。必要なのは接続プールかもしれません。Rspec + Selenium に共有 ActiveRecord 接続を使用してみてはいかがでしょうか?

もう 1 つのオプションは、https://github.com/eventmachine/eventmachine を使用し、DB アクセスがコールバック ブロック (リアクター内) で非ブロッキングな方法で発生するように EM.defer ブロックでタスクを実行することです。

あるいは、より堅牢なソリューションとして、Beantalkd などの軽量のバックグラウンド処理キューを使用します。その他のオプションについては https://www.ruby-toolbox.com/categories/Background_Jobs を参照してください。これが私の主な推奨事項です。

編集、

また、おそらく 200 コアはないので、200 以上の並列スレッドと DB 接続を作成しても、実際にはプロセスが高速化されません (実際には速度が低下します)。問題を複数のコアに分割する方法を見つけられるかどうかを確認してください。コアの数 + 1 に等しい値を設定し、この方法で問題を解決します。

これはおそらくあなたの問題に対する最も簡単な解決策です