0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RailsでSQLite3を複数Thread/Processから同時WriteするときにBusyExceptionを回避

Posted at

TL;DR

RailsのDBにSQLite3を使ってしまうと,Redis等々で同時WriteするとSQLite3::BusyExceptionが発生する.
database.ymltimeoutも効かぬらしい.
仕方がないので自分でプロセス横断の排他Lockを実装する.

やったこと

ActiveRecord.save/.save!でfalseが返ったり例外発生したりするので,成功するまでRetryするとかしても良かったかもしれませんが,あまり格好良くないので,排他Lock制御します.

Rubyの排他Lockの実装自体はぐぐるといっぱい出てくるので,それらを参考に↓のようなblockを排他実行させるClassを用意しました.

LockBlock.rb
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に用意します.

app/models/application_record.rb
def safe_save!
  LockBlock::locked(`db_lock`) {
    self.save!
  }
end

あとは各Modelの.save/.save!やってるところをsafe_save!に置き換えれば完了.

これでいいのか疑問

ひとまず ↑ の対応を入れてRedis使って平行動作させて様子見してますが,FileのLock/Unlockのアトミック性とかよくわかっておらず,これで100%保証されてるのかどうかが不明...

# そもそも`SQLite3やめいとかは言わないように...
# 最初はシンプル機能のお手軽Appになるはずやったんや...

---///

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?