Rails のバッチ処理などで、複数プロセスをまたいで同じ処理を並行して走らせたくないとき、単一サーバであればファイルのロックを使うのが簡単です。
ソース
#{RAILS_ROOT}/lib/batch_lock.rb
という名前で下記を保存する。
# 同一処理を並行で走らせない様にするモジュール
class BatchLockedException < Exception; end
module BatchLock
# 処理を排他ロックして走らせる
def self.run(batch_name)
FileUtils.mkdir_p("#{Rails.root}/tmp/batch_lock")
# batch_name をスコープとして排他処理される。
File.open("#{Rails.root}/tmp/batch_lock/#{batch_name}", 'w') do |f|
if f.flock(File::LOCK_EX | File::LOCK_NB)
yield
else
raise BatchLockedException.new
end
end
end
# 処理が走っているか確認する
def self.running?(batch_name)
FileUtils.mkdir_p("#{Rails.root}/tmp/batch_lock")
# ロック用のファイル名は、バッチごとにユニークにする。
File.open("#{Rails.root}/tmp/batch_lock/#{batch_name}", 'w') do |f|
!f.flock(File::LOCK_EX | File::LOCK_NB)
end
end
end
使い方
同時に走らせたくない処理をブロックで実行する。
begin
BatchLock.run(:go_to_park) do
# ユーザがみんな公園に行く
User.each(&:go_to_park)
end
rescue BatchLockedException
Rails.logger.error "既にバッチが走っています"
end
今バッチが走ってるか確認する。
puts BatchLock.running?(:go_to_park) ? '実行中' : '実行中ではありません'