seed_fuを使ったseed投入をマルチスレッドで実行しようとして以下のようなコードを書きました。

# frozen_string_literal: true

SEED_PATH = "db/seeds/production"

# 同時に動くthreadの数をconnection_poolの数に制限する
LOCKS = Queue.new
ActiveRecord::Base.connection_pool.size.times { LOCKS.push :lock }

def seed_thread(seed_file_name, *depend_thraeds)
  Thread.new do
    depend_thraeds.each(&:join)
    lock = LOCKS.pop
    ActiveRecord::Base.connection_pool.with_connection { SeedFu.seed(SEED_PATH, seed_file_name) }
    LOCKS.push lock
  end
end

{}.tap do |t|
  t[:region]     = seed_thread %r{/region.rb}
  t[:prefecture] = seed_thread %r{/prefecture.rb}, t[:region]
  t[:aaa]        = seed_thread %r{/aaa.rb}
  t[:bbb]        = seed_thread %r{/bbb.rb}
  t[:ccc]        = seed_thread %r{/ccc.rb}, t[:aaa], t[:bbb]
  t[:ddd]        = seed_thread %r{/ddd.rb}, t[:aaa], t[:ccc]
  t[:eee]        = seed_thread %r{/eee.rb}
  # 実際にはもっとテーブル数は多い
end.values.each(&:join)

これを実行すると ActiveRecord::ConnectionTimeoutError が発生することがあります。
発生確率はActiveRecord::Base.connection_poolの数を1にすると100%、5にすると体感で50%くらいです。

実行中のthreadの数 = connection_poolの数のときlockをかけて ActiveRecord::Base.connection_pool.with_connection {} を実行させないつもりのコードなのですが、なぜ ActiveRecord::ConnectionTimeoutError が発生してしまうのでしょうか?

ご教授ください。

  • ruby: 2.5.1
  • rails: 5.2.0
  • seed_fu: 2.3.9

以下のようにしました。

# frozen_string_literal: true

SEED_PATH = "db/seeds/production"
THREAD_NUM = 4

LOCKS = Queue.new
THREAD_NUM.times { LOCKS.push :lock }

def seed_thread(seed_file_name, *depend_thraeds)
  Thread.new do
    depend_thraeds.each(&:join)
    lock = LOCKS.pop
    begin
      ActiveRecord::Base.connection_pool.with_connection { SeedFu.seed(SEED_PATH, seed_file_name) }
    rescue ActiveRecord::ConnectionTimeoutError
      STDERR.puts "retry #{seed_file_name}"
      sleep 1
      retry
    end
    LOCKS.push lock
  end
end

{}.tap do |t|
  t[:region]     = seed_thread %r{/region.rb}
  t[:prefecture] = seed_thread %r{/prefecture.rb}, t[:region]
  t[:aaa]        = seed_thread %r{/aaa.rb}
  t[:bbb]        = seed_thread %r{/bbb.rb}
  t[:ccc]        = seed_thread %r{/ccc.rb}, t[:aaa], t[:bbb]
  t[:ddd]        = seed_thread %r{/ddd.rb}, t[:aaa], t[:ccc]
  t[:eee]        = seed_thread %r{/eee.rb}
  # 実際にはもっとテーブル数は多い
end.values.each(&:join)