プラットフォームに依存しないファイルロック?
-
21-08-2019 - |
質問
私は非常に計算量の多い科学的なジョブを実行しており、時々結果を吐き出します。基本的に同じことを何度もシミュレートするだけの作業なので、異なる OS を使用する複数のコンピュータに分割されます。すべてのコンピュータは NFS/Samba 経由で同じファイルシステムを参照できるため、これらすべてのインスタンスからの出力を同じファイルに送りたいと考えています。制約は次のとおりです。
- 安全な同時追加を許可する必要があります。別のコンピューター上の他のインスタンスが現在ファイルに追加されている場合はブロックする必要があります。
- パフォーマンスは ない カウント。各インスタンスの I/O は 1 分あたりわずか数バイトです。
- シンプルさは重要です。この目的は (純粋な好奇心以外に)、すべてのインスタンスが別のファイルに書き込んだり、これらのファイルを手動でマージしたりするのをやめることです。
- ファイルシステムの詳細に依存してはなりません。NFS または Samba マウント上の不明なファイルシステムで動作する必要があります。
念のため言っておきますが、私が使用している言語は D です。調べてみたところ、標準ライブラリにはこれを行うものは何もありませんでした。D 固有の回答と、言語に依存しない一般的な回答の両方が完全に受け入れられ、評価されます。
解決
NFS では、クライアント側のキャッシュと古いデータに関するいくつかの問題に直面します。以前、NFS 上で動作する OS に依存しないロック モジュールを作成したことがあります。[datafile].lock ファイルを作成するという単純な考え方は、NFS ではうまく機能しません。これを回避するための基本的な考え方は、ロック ファイル [datafile].lock を作成することです。これは、ファイルがロックされていないことを意味し、ロックを取得したいプロセスがファイルの名前を [datafile].lock.[ のような別の名前に変更することを意味します。ホスト名].[pid]。名前変更はアトミックな操作であり、NFS 上で十分に機能し、ロックの排他性を保証します。残りは基本的に、ロックを解放してロック ファイルの名前を [datafile].lock に戻す前にプロセスが終了した場合に備えて、一連のフェイル セーフ、ループ、エラー チェック、およびロックの取得です。
他のヒント
古典的な解決策は、ロック ファイル、より正確にはロック ディレクトリを使用することです。すべての一般的な OS では、ディレクトリの作成はアトミックな操作であるため、ルーチンは次のようになります。
- 固定の場所に固定の名前でロック ディレクトリを作成してみてください
- 作成が失敗した場合は、1 秒ほど待ってから再試行します。成功するまで繰り返します。
- データを実際のデータ ファイルに書き込みます
- ロックディレクトリを削除する
これは、CVS などのアプリケーションで多くのプラットフォームで長年使用されてきました。唯一の問題は、書き込み中およびロックを解除する前にアプリがクラッシュする場合に、まれに発生します。
ファイルと他のコンピュータの間に単純なサーバーを構築するだけではどうでしょうか?
データ形式を変更したい場合は、すべてのクライアントを変更する必要はなく、サーバーのみを変更するだけで済みます。
私の考えでは、サーバーを構築することは、ネットワーク ファイル システムを使用するよりもはるかに簡単だと思います。
ひねりを加えてファイルをロックする
他の回答で述べたように、最も簡単な方法は、データファイルと同じディレクトリにロックファイルを作成することです。
複数の PC から同じファイルにアクセスできるようにしたい場合、私が考える最良の解決策は、現在データ ファイルに書き込んでいるマシンの識別子を含めることです。
したがって、データ ファイルに書き込むシーケンスは次のようになります。
ロックファイルが存在するかどうかを確認する
ロック ファイルがある場合は、その内容に私の識別子が含まれていることを確認して、そのファイルを所有しているのが私であるかどうかを確認します。
その場合は、データ ファイルに書き込んでからロック ファイルを削除してください。
そうでない場合は、1 秒またはランダムな短い時間を待って、サイクル全体を再試行してください。ロック ファイルがない場合は、私の識別子でロック ファイルを作成し、競合状態を避けるためにサイクル全体を再試行します (ロック ファイルが本当に私のものであることを再確認します)。
識別子とともにタイムスタンプをロック ファイルに記録し、それが指定されたタイムアウト値より古いかどうかを確認します。
タイムスタンプが古すぎる場合は、データ ファイルに書き込みを行っている PC の 1 台がクラッシュしたか、接続が失われた可能性があるため、ロック ファイルが古いと考えて削除してください。
別の解決策
データ ファイルの形式を制御している場合は、ファイルがロックされているかどうかを記録するための構造体をファイルの先頭に予約することができます。
この目的のためにバイトを予約するだけの場合、たとえば、次のように仮定できます。 00
はデータ ファイルがロックされていないことを意味し、その他の値は現在データ ファイルに書き込みを行っているマシンの識別子を表します。
NFS に関する問題
OK、Jiri Klouda が正しく指摘したので、いくつか追加します。 NFS はクライアント側のキャッシュを使用します その結果、実際のロック ファイルは不定の状態になります。
この問題を解決するには、いくつかの方法があります。
NFS ディレクトリをマウントします。
noac
またはsync
オプション。これは簡単ですが、クライアントとサーバー間のデータの一貫性が完全に保証されているわけではないため、あなたの場合は問題ないかもしれませんが、まだ問題が発生する可能性があります。ロック ファイルまたはデータ ファイルを開くには、
O_DIRECT
, 、O_SYNC
またはO_DSYNC
属性。これにより、キャッシュが完全に無効になるはずです。
これによりパフォーマンスは低下しますが、一貫性は確保されます。あなた 5月 使えるようになる
flock()
データ ファイルをロックしますが、その実装にはむらがあるため、特定の OS が実際に NFS ロック サービスを使用しているかどうかを確認する必要があります。それ以外の場合は、まったく何も起こらない可能性があります。
データ ファイルがロックされている場合、別のクライアントがそのデータ ファイルを開いて書き込みを行うと失敗します。
そうそう、SMB 株では機能しないようなので、忘れたほうが良いでしょう。NFS を使用せず、代わりに Samba を使用してください。ある この主題に関する良い記事 NFS がおそらくあなたの使用シナリオにとって最良の答えではない理由。
この記事では、ファイルをロックするさまざまな方法についても説明します。Jiri の解決策も良いものです。
基本的に、物事をシンプルに保ちたい場合は、複数のマシン間で共有される頻繁に更新されるファイルには NFS を使用しないでください。
何か違います
小規模なデータベース サーバーを使用してデータを保存し、NFS/SMB ロックの問題を完全にバイパスするか、現在の複数のデータ ファイル システムを維持し、結果を連結する小さなユーティリティを作成するだけです。
これは、問題に対する最も安全で簡単な解決策である可能性があります。
D はわかりませんが、ミューテックス ファイルを使用してジョブを実行するとうまくいくかもしれません。以下に役に立つかもしれない疑似コードをいくつか示します。
do {
// Try to create a new file to use as mutex.
// If it's already created, it will throw some kind of error.
mutex = create_file_for_writing('lock_file');
} while (mutex == null);
// Open your log file and write results
log_file = open_file_for_reading('the_log_file');
write(log_file, data);
close_file(log_file);
close_file(mutex);
// Free mutex and allow other processes to create the same file.
delete_file(mutex);
したがって、すべてのプロセスがミューテックス ファイルの作成を試みますが、勝ったプロセスのみが続行できます。出力を書き込んだら、他のプロセスが同じことをできるように、ミューテックスを閉じて削除します。