読者です 読者をやめる 読者になる 読者になる

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

RailsでSQLite3 の lock timeout が発生したので少し調べて見ました。
(原因は複数transactionが発生してロックかかってただけです)

まずは

みんな大好きstackoverflow。
ruby on rails - SQLite3::BusyException - Stack Overflow

一時的な対策ではあるが、 database.yml の timeout 値を変更することでBusyExceptionsを減らすことができるかもねという記載があります。

ソースを見て見ます

activerecord

# activerecord-5.0.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:30
      db = SQLite3::Database.new(
        config[:database].to_s,
        results_as_hash: true
      )

      db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]

SQLite3のbusy_timeout を呼んでいるだけのようです。

sqlite3

# sqlite3-1.3.13/lib/sqlite3/pragmas.rb
    def busy_timeout=( milliseconds )
      set_int_pragma "busy_timeout", milliseconds
    end

set_int_pragma に値を渡しているだけ。

    def set_int_pragma( name, value )
      execute( "PRAGMA #{name}=#{value.to_i}" )
    end

PRAGMA〜 という文字列で実行しているだけでした。

PRAGMAステートメント

全く知らなかったのですが、SQLite3固有の拡張コマンドでした。

 
busy_timeout は以下。
Pragma statements supported by SQLite

他にもcache sizeを変更したり同期タイミング(default_synchronous)を変更することで50倍以上高速になるそうです。
色々チューニングできそうです。