flock を使わない排他制御
man ページを見ると flock(2) は NFS 上のファイルのロックをしないとある。つまりそういうことなのだろう。
しかしそれで排他を諦めるわけにはいかない。flock(2) の代替として提示されている fcntl(2) によるロックは Ruby からは使用できない。できるのかもしれないが引数に渡す flock 構造体とやらの扱いが不明なので断念。
ロックディレクトリ方式も万全ではないことは確認済みだ。というか MOE は今その万全ではない状態で動いていて排他の破れも度々ログに記録されている。これまでクリティカルな事故は一度だけだが今後も十分起こり得るという状態は精神衛生上よろしくないので早急に何とかしたいと常々思っていた。
そんな経緯で、以前どこかで目にしたリネームロックというものを試してみた。
ロックファイルを用意し、ロックを掛ける時はロックファイルそのものをリネームしてしまうというやり方だが、実はそれ以上の内容はとんと覚えていない。というかその時は全く興味がなかったので読み飛ばしてしまったのだろう。仕方がないので車輪を再発明するしかない。
LOCKFILE = '.lockfile' LOCKEDFILE = '.lockfile.locked' def lock loop do begin File.rename(LOCKFILE, LOCKEDFILE) open(LOCKEDFILE, 'w').puts $$ break rescue Exception sleep 1 end begin pid = open(LOCKEDFILE, 'r').read.to_i unless pid == $$ next if Process.kill(0, pid) rescue false end File.rename(LOCKEDFILE, LOCKFILE) rescue Exception end end end def unlock File.rename(LOCKEDFILE, LOCKFILE) end
テストしてみた範囲ではこれで相当頑丈になったようだ。
心配なのはロックしっ放しでプロセスが死んだ場合の後始末。リネーム後のファイルから該当プロセス ID を取得するやり方がイマイチ美しくないように思えるが。さて。