ちょっとハマったのでメモ。
ファイルを flock()
で排他的にロックするために次のようにすると成功します。
% ruby -e 'File.open("hoge").flock(File::LOCK_EX); puts "OK"' OK
が、NFS 上で同じことをやると失敗します。
% ruby -e 'File.open("hoge").flock(File::LOCK_EX); puts "OK"' -e:1:in `flock': Bad file descriptor @ rb_file_flock - hoge (Errno::EBADF) from -e:1:in `<main>'
7年前に書いたんですが、Linux は NFS ファイルシステムに対して flock()
すると fcntl(F_SETLK)
を使います。
で、fcntl(F_SETLK)
は flock()
と異なり、排他ロックをするにはファイルを書き込み可のモードでオープンしておかないといけません。
通常のローカルファイルシステムでも読み込み専用でオープンしたファイルに fcntl(F_SETLK)
を使って排他ロックを行えば同じエラーが発生します。
% ruby -rfcntl -e 'File.open("hoge").fcntl(Fcntl::F_SETLK, [Fcntl::F_WRLCK, 0].pack("ss"))' -e:1:in `fcntl': Bad file descriptor @ rb_fcntl - hoge (Errno::EBADF) from -e:1:in `<main>'
書き込みできるモードでオープンしておけばエラーにはなりません。
% ruby -rfcntl -e 'File.open("hoge", "r+").fcntl(Fcntl::F_SETLK, [Fcntl::F_WRLCK, 0].pack("ss")); puts "OK"' OK
つまり、NFS 上でも書き込みできるモードでファイルをオープンしておけば flock()
で排他制御できます。
% ruby -e 'File.open("hoge", "r+").flock(File::LOCK_EX); puts "OK"' OK
ということで、flock(LOCK_EX)
する場合は書き込みできるモードでファイルをオープンしておいた方が無難というお話でした。