あなたはNFSについて尋ねました。この種類のコードは、NFSで壊れる可能性があります。これnoclobber
は、2つの別個のNFS操作(ファイルが存在するかどうかを確認し、新しいファイルを作成する)を含み、2つの別個のNFSクライアントからの2つのプロセスが、両方が成功する競合状態になる可能性があるためです(どちらもB.part
まだ存在しないことを確認してから、正常に作成します。その結果、お互いが上書きされます。)
あなたが書いているファイルシステムがnoclobber
アトミックに何かをサポートするかどうかについて、一般的なチェックを実際に行う必要はありません。NFSであるかどうかにかかわらず、ファイルシステムのタイプを確認できますが、これはヒューリスティックであり、必ずしも保証されるものではありません。SMB / CIFS(Samba)などのファイルシステムでも同じ問題が発生する可能性があります。FUSEを介して公開するファイルシステムは、正しく動作する場合と動作しない場合がありますが、そのほとんどは実装に依存しています。
おそらくより良いアプローチは、B.part
(他のエージェントとの連携により)一意のファイル名を使用することにより、ステップでの衝突を回避することですnoclobber
。これにより、に依存する必要がなくなります。たとえば、ファイル名の一部として、ホスト名、PID、およびタイムスタンプ(おそらくランダムな番号)を含めることができます。ホストの特定のPIDの下で実行される単一のプロセスが常に存在するため、これは一意性を保証します。
したがって、次のいずれかになります。
test -f B && continue # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
# Maybe check for existance of B again, remove
# the temporary file and bail out in that case.
mv B.part."$unique" B
# mv (rename) should always succeed, overwrite a
# previously copied B if one exists.
または:
test -f B && continue # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
if ln B.part."$unique" B ; then
echo "Success creating B"
else
echo "Failed creating B, already existed"
fi
# Both cases require cleanup.
rm B.part."$unique"
したがって、2つのエージェント間に競合状態がある場合、エージェントは両方とも操作を続行しますが、最後の操作はアトミックであるため、BがAの完全なコピーとともに存在するか、Bが存在しません。
コピー後とmv
or ln
操作の前にもう一度チェックすることで、レースのサイズを縮小できますが、まだ小さな競合状態があります。ただし、競合状態に関係なく、両方のプロセスがAから(または有効なファイルからのコピーを起点として)作成しようとしていると仮定して、Bの内容は一貫している必要があります。
の最初の状況でmv
は、競合が存在する場合、rename(2)が既存のファイルをアトミックに置き換えるため、最後のプロセスが勝者となります。
newpathがすでに存在する場合は、アトミックに置き換えられるため、newpathにアクセスしようとする別のプロセスがそれを見つけられなくなることはありません。[...]
場合newpathがが存在しますが、操作が何らかの理由で失敗し、rename()
インスタンス残すことを保証newpathをする場所に。
したがって、その時点でBを消費しているプロセスでは、このプロセス中に異なるバージョンのiノード(異なるiノード)が表示される可能性があります。ライターがすべて同じコンテンツをコピーしようとしていて、リーダーが単にファイルのコンテンツを消費しているだけの場合、問題はないかもしれません。同じコンテンツのファイルに対して異なるiノードを取得しても、同じように満足します。
ハードリンクを使用した2番目のアプローチはより良いように見えますが、多くの同時クライアントからNFSのタイトループでハードリンクを使って実験を行い、成功を数えたことを覚えています。同じ宛先で同じ操作を同時に実行すると、どちらも成功したようです。(この動作は特定のNFSサーバー実装であるYMMVに関連していた可能性があります。)いずれにせよ、それはおそらく同じ種類の競合状態であり、重いファイルがある場合、同じファイルに対して2つの別々のiノードを取得することになります。これらの競合状態をトリガーするライター間の同時実行性。ライターが一貫していて(どちらもAからBにコピーする)、リーダーがコンテンツのみを消費している場合は、それで十分かもしれません。
最後に、ロックについて説明しました。残念ながら、少なくともNFSv3ではロックが大幅に不足しています(NFSv4については不明ですが、それも良くないでしょう)。ロックを検討している場合は、おそらく、実際のファイルのコピーですが、それは混乱を招き、複雑で、デッドロックなどの問題が発生しやすいので、回避することをお勧めします。
NFSのアトミック性の主題の背景については、ロックを回避し、NFSでも確実に動作するように作成されたMaildirメールボックス形式をお読みください。それはどこでもユニークなファイル名を維持することによってそうします(それで、最後に最後のBを得ることすらありません。)
Maildir ++形式は、Maildir ++形式を拡張してメールボックスクォータのサポートを追加し、メールボックス内の固定名でファイルをアトミックに更新することで(Bに近い可能性があるため)、Maildir ++が試行すると思います。追加することは、NFSでは本当に安全ではありませんが、これと同様の手順を使用する再計算アプローチがあり、アトミックな置き換えとして有効です。
うまくいけば、これらすべてのポインタが役立つでしょう!