RailsでSQLite3を複数Thread/Processから同時WriteするときにBusyExceptionを回避
TL;DR
Rails
のDBにSQLite3
を使ってしまうと,Redis
等々で同時WriteするとSQLite3::BusyException
が発生する.
database.yml
のtimeout
も効かぬらしい.
仕方がないので自分でプロセス横断の排他Lockを実装する.
やったこと
ActiveRecord.save/.save!
でfalseが返ったり例外発生したりするので,成功するまでRetryするとかしても良かったかもしれませんが,あまり格好良くないので,排他Lock制御します.
Ruby
の排他Lockの実装自体はぐぐるといっぱい出てくるので,それらを参考に↓のようなblockを排他実行させるClassを用意しました.
require 'tmpdir'
class LockBlock
class << self
def locked(lock_file_name)
File::open(File::join(Dir::tmpdir, lock_file_name), 'w') { |file|
begin
file.flock(File::LOCK_EX)
yield
ensure
file.flock(File::LOCK_UN)
end
}
end
end
end
この排他Lockで守った状態でActiveRecord.save/.save!
するためのMethodをApplicationRecord
に用意します.
def safe_save!
LockBlock::locked(`db_lock`) {
self.save!
}
end
あとは各Modelの.save/.save!
やってるところをsafe_save!
に置き換えれば完了.
これでいいのか疑問
ひとまず ↑ の対応を入れてRedis
使って平行動作させて様子見してますが,FileのLock/Unlockのアトミック性とかよくわかっておらず,これで100%保証されてるのかどうかが不明...
# そもそも`SQLite3やめいとかは言わないように...
# 最初はシンプル機能のお手軽Appになるはずやったんや...
---///
Author And Source
この問題について(RailsでSQLite3を複数Thread/Processから同時WriteするときにBusyExceptionを回避), 我々は、より多くの情報をここで見つけました https://qiita.com/fezrestia/items/4f578e92d58c27e3a34c著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .