连接池问题与rufus-scheduler中的ActiveRecord对象有关

时间:2012-06-28 16:17:35

标签: ruby-on-rails activerecord rufus-scheduler

我正在使用rufus-scheduler来运行一些频繁的作业,这些作业使用ActiveRecord对象执行一些不同的任务。如果存在任何类型的网络或postgresql打嗝,即使在恢复之后,所有线程都会抛出以下错误,直到重新启动该进程:

ActiveRecord :: ConnectionTimeoutError(无法在5秒内获得数据库连接(等待5.000122687秒)。最大池大小当前为5;考虑增加它。

重新启动postgres可以轻松复制错误。我已经尝试过使用游泳池大小(最多15个),但没有运气。

这让我相信连接只是处于陈旧状态,我认为可以通过调用clear_stale_cached_connections!来解决这个问题。

有更可靠的模式吗?

传递的块是一个简单的选择和更新活动记录调用,并且恰好与AR对象有关。

鲁弗斯的工作:

scheduler.every '5s' do
  db do
    DataFeed.update  #standard AR select/update
  end
end

包装器:

  def db(&block)
    begin
      ActiveRecord::Base.connection_pool.clear_stale_cached_connections!
      #ActiveRecord::Base.establish_connection    # this didn't help either way
      yield block
    rescue Exception => e
      raise e
    ensure
      ActiveRecord::Base.connection.close if ActiveRecord::Base.connection
      ActiveRecord::Base.clear_active_connections!
    end
  end

3 个答案:

答案 0 :(得分:13)

Rufus调度程序为每个作业启动一个新线程。 另一方面,ActiveRecord无法共享线程之间的连接,因此需要为特定线程分配连接。

当您的线程尚未建立连接时,它将从池中获取一个连接。 (如果池中的所有连接都在使用中,它将等待,直到从另一个线程返回一个。最终超时并抛出ConnectionTimeoutError)

完成后,您有责任将其恢复到池中,在Rails应用程序中,这是自动完成的。但是如果你正在管理你自己的线程(就像rufus那样),你必须自己做。

幸运的是,有一个api: 如果将代码放在with_connection块中,它将从池中获得连接,并在完成后释放它

ActiveRecord::Base.connection_pool.with_connection do
  #your code here
end

在你的情况下:

def db
  ActiveRecord::Base.connection_pool.with_connection do
    yield
  end
end

应该做的伎俩......

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ConnectionPool.html#method-i-with_connection

答案 1 :(得分:1)

原因可能是你有很多线程正在使用所有连接,如果DataFeed.update方法需要超过5秒,那么你的块可以重叠。

尝试

scheduler.every("5s",  :allow_overlapping => false) do
#...
end

同时尝试释放连接而不是关闭它。

 ActiveRecord::Base.connection_pool.release_connection

答案 2 :(得分:-1)

我真的不知道rufus-scheduler,但我有一些想法。

第一个问题可能是rufus-scheduler上没有正确检出数据库连接的错误。如果是这种情况,唯一的解决方案是手动清除陈旧连接,并告知rufus-scheduler作者您的问题。

可能发生的另一个问题是你的DataFeed操作需要很长时间,因为它每5次执行一次Rails就会耗尽数据库连接,但这种可能性不大。