ActiveRecord,Sqlite3和BusyException

时间:2011-05-12 19:55:47

标签: ruby-on-rails ruby sqlite

我有一个配置了混合mysql和sqlite3数据库连接的rails应用程序,并且为了将特定模型与sqlite3相关联,我在每个类定义中添加了“establish_connection'sqlite_db_config_name”行。

当我尝试单独保存任何sqlite3连接的模型对象时,保存成功,但是当我尝试保存由其他对象组成的对象时(通过has_many),我得到一个BusyException。我有一种感觉,因为每个对象都有自己的数据库连接,顶级对象锁定数据库然后调用成员对象的保存方法,他们无法获取锁。

我假设有一种方法可以完成这项工作,而且我正在使用不正确的connect_connection。

其他人遇到过这个吗?

database.yml config:

dev的:

development:
  adapter: mysql
  database: maindb
  username: root
  password:
  host: localhost

sqlite:
  adapter: sqlite3
  database: db/db.sqlite3
  timeout: 15000

模型定义:

class Foo < ActiveRecord::Base
  establish_connection 'sqlite'
  belongs_to :bar
end

class Bar < ActiveRecord::Base
  establish_connection 'sqlite'
  has_many :foo  
  def addFoo(item)
    self.foos << item
  end
end

class MysqlModel < ActiveRecord::Base
end

其他:

Ruby 1.8.7
Rails 2.3.4
Ubuntu 10.04

更新

我尝试使用继承来隔离单个类中的establish_connection语句,这是基于rails doc的解释:“此功能是通过在ActiveRecord :: Base中保留连接池来实现的,该连接池是由类索引的哈希值。请求连接,retrieve_connection方法将上升到类层次结构,直到在连接池中找到连接。“但由于某种原因,rails将sqlite连接类的子类与默认的mysql连接相关联。所以我放弃了尝试与sqlite的has_many / belongs_to关系,并对我的模型进行了规范化。

2 个答案:

答案 0 :(得分:1)

我认为“establish_connection'sqlite”会导致问题。尝试删除它。 SQLite只允许一个连接用于写入每个数据库(文件)

如果对数据库执行多次写操作而不关闭它,则会导致该异常

Rails可以高效自动地处理连接,因此我认为我们不需要单独建立连接。

答案 1 :(得分:0)

我在sqlite3 ruby​​扩展程序上发现了一个死锁,并在此处修复它:继续使用它,看看这是否解决了你的问题。


    https://github.com/dxj19831029/sqlite3-ruby

我打开了一个拉取请求,没有他们的回复。

无论如何,如sqlite3本身所述,预计会有一些繁忙的异常。

请注意此条件:sqlite busy


    The presence of a busy handler does not guarantee that it will be invoked when there is 
    lock contention. If SQLite determines that invoking the busy handler could result in a 
    deadlock, it will go ahead and return SQLITE_BUSY or SQLITE_IOERR_BLOCKED instead of 
    invoking the busy handler. Consider a scenario where one process is holding a read lock 
    that it is trying to promote to a reserved lock and a second process is holding a reserved 
    lock that it is trying to promote to an exclusive lock. The first process cannot proceed 
    because it is blocked by the second and the second process cannot proceed because it is 
    blocked by the first. If both processes invoke the busy handlers, neither will make any 
    progress. Therefore, SQLite returns SQLITE_BUSY for the first process, hoping that this 
    will induce the first process to release its read lock and allow the second process to 
    proceed.

如果您遇到此情况,超时将不再有效。要避免它,请不要将select放在begin / commit中。或者使用独占锁来开始/提交。

希望这会有所帮助。 :)