Caught ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked: SQL

ActiveRecord で SQLite3 を利用していた場合に以下のエラーが発生しました。

Caught ActiveRecord::StatementInvalid: SQLite3::BusyException:
database is locked: SQL
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract_adapter.rb:150:in `log'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/sqlite_adapter.rb:132:in `execute'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/sqlite_adapter.rb:345:in `catch_schema_changes'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/sqlite_adapter.rb:132:in `execute'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/sqlite_adapter.rb:256:in `select'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all_without_query_cache'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract/query_cache.rb:55:in `select_all'


原因は、SQLite3 のファイルに同時にアクセスした場合に、ロックが競合していたためでした。


対策は、ActiveRecord を継承しているクラスでタイムアウトを設定することです。
以下の場合 10,000(ミリ秒) 待ちます。

ActiveRecord::Base.establish_connection(
  :adapter => 'sqlite3',
  :database => '/hoge/var.sqlite3',
+ :timeout => 10000
)