ファイルをトランザクション方式でコピーする方法は?

ファイルをトランザクション方式でコピーする方法は?

別のファイルシステムにある可能性があるAからBにファイルをコピーしたいです。

いくつかの追加要件があります。

  1. コピーは全部または全部であり、競合時にファイルBの一部または破損した部分を残しません。
  2. 既存のファイルBを上書きしないでください。
  3. 同じコマンドの同時実行と競合しないでください。最大1つだけ成功できます。

私はこれが近いと思います:

cp A B.part && \
ln B B.part && \
rm B.part

ただし、3. B.partが存在すると(-nフラグがあっても)違反し、cpは失敗しません。その後 1. 他のプロセスが cp を「勝ち」、所定の場所にリンクされたファイルが不完全な場合、失敗することがあります。 B.partが無関係なファイルである可能性がありますが、他の隠された名前を試さずにこの場合失敗することを嬉しく思います。

私はbash noclobberが役に立つと思います。これはまったく機能しますか? bashバージョンを必要としない方法はありますか?

#!/usr/bin/env bash
set -o noclobber
cat A > B.part && \
ln B.part B && \
rm B.part

フォローアップを実行すると、一部のファイルシステムがとにかく失敗することがわかります(NFS)。これらのファイルシステムを検出する方法はありますか?

関連しているが同じではない他の質問:

ファイルシステム間のおおよその原子移動?

私のfsにmv原子がありますか?

eMMCのtempfsからext4パーティションにファイルとディレクトリをアトミックに移動する方法はありますか?

https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html

答え1

rsyncタスクを実行します。デフォルトでは、一時ファイルが作成されO_EXCL(使用している場合にのみ無効--inplace)、renamedターゲットファイルが上書きされます。--ignore-existingBがある場合は上書きしないために使用されます。

実際、私はext4、zfs、さらにはNFSマウントでも問題が発生したことがありません。

答え2

大丈夫です、noclobber標準機能です

答え3

NFSについて質問しました。これらのコードには、スキャンにnoclobber2つの別々のNFS操作(ファイルの存在確認、新しいファイルの作成)が含まれ、2つの別々のNFSクライアントで2つのプロセスが競合状態になる可能性があるため、NFSで中断される可能性があります。成功(両方がすでに存在しないことを確認し、正常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コピー後、または操作の前に再確認することで競合のサイズを減らすことができますが、lnまだいくつかの競合状態があります。ただし、競合状態に関係なく、両方のプロセスがAから(またはソースのコピーである有効なファイルから)Bを生成しようとすると、Bの内容は一貫している必要があります。

最初のケースでは、レースがあるmvと最後のプロセスが勝者になります。なぜなら名前変更(2)既存のファイルは自動的に置き換えられます。

もし新しい道すでに存在する場合は、他のプロセスがアクセスを試みる可能性がないように自動的に置き換えられます。新しい道欠けているものが見つかります。 [...]

もし新しい道存在しますが、何らかの理由で操作が失敗し、rename()インスタンスを残すことが保証されます。新しい道所定の位置に。

したがって、当時Bを使用するプロセスは、プロセス中に異なるバージョン(別のinode)を見る可能性が高いです。作成者が同じコンテンツをコピーしようとしていて、リーダーがファイル内のコンテンツを使用している場合は、おそらく大丈夫でしょう。

2番目の方法はハードリンクを使用します。そうだより良いですが、多くの同時クライアントでNFSの緊密なループでハードリンクを試して成功を数えたことを覚えています。両方のクライアントが同じ目的で同時にハードリンクジョブを実行しているように見えるいくつかの競合状態があるようです。まあ、どちらも成功したようです。 (この動作はYMMVの特定のNFSサーバー実装に関連している可能性があります。)とにかくこれは同じ種類の競合状態である可能性があり、大容量データの場合は同じ操作に対して2つの別々のファイルを生成できます。ファイル。インデックスノード。作者間の並行性により、これらの競合状態が発生します。作成者が一貫して(すべてAからBにコピー)、読者がコンテンツのみを消費するだけで十分です。

最後に閉鎖について言及しました。残念ながら、NFSv3ではロック機能が非常に不足しています。 (NFSv4についてはわかりませんが、それほど良くはありません。)ロックを検討している場合は、実際のファイルと帯域外の他の分散ロックプロトコルを調べる必要があります。しかし、これは破壊的で複雑で、デッドロックなどの問題が発生しやすいため、避けるのが最善だと思います。


NFSアトミックトピックに関する詳細な背景情報については、以下を読んでください。Maildir メール形式NFSでもロックを防ぎ、安定して動作するように作られています。どこでも一意のファイル名を保持してこれを行います(したがって、最後にBは表示されません)。

特定のケースにもっと興味深いかもしれません。Maildir++ 形式Maildirを拡張してメールボックスクォータのサポートを追加し、メールボックス内の固定名のファイルを自動的に更新することでこれを行います(これはBに近づくことがあります)。していない追加を試しているようです。これと同様のプロセスを使用する再計算方法として原子置換として機能します。

これらすべての指示が役立つことを願っています!

答え4

cpで実行すると正しい結果が得られますmv。これにより、「B」が「A」の新しいコピーに変わるか、「B」が以前のままになります。

cp A B.tmp && mv B.tmp B

既存に合わせて更新されましたB

cp A B.tmp && if [ ! -e B ]; then mv B.tmp B; else rm B.tmp; fi

100%原子ではありませんが、ほぼ同じです。 2つが実行される競合条件があります。両方ともif同時にテストに入り、両方がB存在しないことを確認してから、両方を実行しますmv

関連情報