NFS file lock consistency design principle
ファイルロック
ファイルロックは、ファイルシステムの最も基本的な機能の一つです。ファイルロックを利用することで、アプリケーションは他のアプリケーションからのファイルへの同時アクセスを制御できます。UNIX 系システムの標準ネットワークファイルシステムである NFS は、開発プロセスの中で徐々にネイティブにファイルロックをサポートしてきました(NFSv4 以降)。NFS は 1980 年代の誕生以来、NFSv2、NFSv3、NFSv4 の 3 つのバージョンをリリースしてきました。NFSv4 の最大の変更点は「ステート」です。特定の操作では、サーバーが関連するステートを維持する必要があります。たとえば、ファイルロックです。クライアントがファイルロックを申請すると、サーバーはファイルロックのステートを維持する必要があります。そうしないと、他のクライアントからの競合するアクセスを検出できません。NFSv3 の場合、ファイルロック機能を実現するには NLM の支援が必要ですが、両者がうまく連携できない場合があり、エラーが発生しやすくなります。一方、NFSv4 はステートフルプロトコルとして設計されており、単独でファイルロック機能を実現できるため、NLM プロトコルは不要です。
アプリケーションインターフェイス
アプリケーションは、fcntl() または flock() システムコールを通じて NFS ファイルロックを管理できます。以下は、NFSv4 を使用して NAS をマウントする際のファイルロック取得の呼び出しプロセスです。
上の図の呼び出しスタックから、NFS ファイルロックの実装ロジックが基本的に VFS レイヤーの設計とデータ構造を再利用していることが容易にわかります。RPC を通じてサーバーからファイルロックを正常に取得した後、locks_lock_inode_wait() 関数が呼び出され、取得したファイルロックが VFS レイヤーに転送されて管理されます。VFS レイヤーのファイルロック設計に関する資料は多数存在するため、ここでの詳細な説明は省略します。
EOS の原理
ファイルロックは典型的な非冪等操作です。ファイルロック操作のリトライとフェールオーバーは、クライアントとサーバー間でファイルロックステータスビューの不整合を引き起こします。NFSv4 は、最大 1 回のみ実行できるメカニズムを設計するために SeqId メカニズムを使用しています。具体的な方法は以下の通りです。
各 open/lock ステートに対して、クライアントとサーバーは独立して同時に seqid を維持します。クライアントがステート変更を引き起こす操作(open/close/lock/unlock/release_lockowner)を開始すると、seqid に 1 を加えてパラメーターとしてサーバーに送信します。クライアントが送信した seqid を R、サーバーが維持している seqid を L とすると、以下のようになります。
1) R == L +1 の場合、合法なリクエストであることを意味し、正常に処理する必要があります。
2) R == L の場合、リクエストのリトライであることを意味し、サーバーはキャッシュされた応答を返します。
3) その他の場合、不正なリクエストであり、アクセスは絶対に禁止されます。
上記のルールに従って、サーバーは操作が正常か、リトライか、不正なリクエストかを判断できます。
この方法により、各ファイルロック操作がサーバー側で最大 1 回のみ実行されることが保証され、RPC リトライによる繰り返し実行の問題が解決されます。しかし、これだけでは不十分です。たとえば、LOCK 操作の送信後、呼び出しスレッドがシグナルによって割り込まれ、その後サーバーが LOCK 操作を正常に受け入れて実行した場合、サーバーはクライアントがロックを保持していることを記録しますが、クライアントは割り込みのためにロックを維持しません。これにより、クライアントとサーバー間でロックステータスビューの不整合が発生します。したがって、クライアントも異常なシナリオの処理に協力する必要があり、最終的にファイルロックビューの一貫性を達成します。
例外処理
前のセクションの分析から、クライアントがファイルビューの一貫性を確保するために異常なシナリオの処理に協力する必要があることがわかりました。では、クライアント設計者は主にどのような協調設計を行ったのでしょうか。現在、クライアントは主に SunRPC と NFS プロトコルの 2 つの側面の相互協力によってこの問題を解決しています。以下では、これら 2 つの側面の設計がどのようにファイルロックステータスビューの一貫性を確保しているかを紹介します。
SunRPC 設計
SunRPC は、Sun がリモートプロシージャコール専用に設計したネットワーク通信プロトコルです。ここでは、ファイルロックビューの一貫性を確保するという側面から、SunRPC 実装レベルの設計概念を理解します。
1) クライアントは int32 型 xid を使用して、上位ユーザーによって開始された各リモートプロシージャコールプロセスを識別します。各リモートプロシージャコールの複数の RPC リトライは同じ xid 識別子を使用し、これにより、いずれかの応答が上位層にリモートプロシージャコールが成功したことを通知でき、サーバーがリモートプロシージャコールの実行に長時間かかっても結果を取得できることを保証します。これは従来の netty/mina/brpc などと同じです。各 RPC には独立した xid/packetid が必要です。
2) サーバーは DRC(Duplicate Request Cache)を設計し、最近実行された RPC 結果をキャッシュします。RPC を受信すると、まず xid を通じて DRC キャッシュを検索します。ヒットした場合、RPC がリトライ操作であることを示し、キャッシュされた結果を直接返すことができます。これにより、RPC リトライによる繰り返し実行の問題がある程度回避されます。xid の再利用によって DRC キャッシュが予期しない結果を返すのを避けるために、開発者は以下の設計を通じて再利用によるエラーの発生確率をさらに効果的に低減しています。
a) クライアントが新しい接続を確立する際、初期 xid はランダム値を採用します。
b) サーバー側の DRC はリクエストの検証情報を追加で記録し、キャッシュヒット時に同時に検証されます。
3) クライアントはサーバーからの応答を取得する前に無制限にリトライを許可され、呼び出し元がサーバーの確定的な実行結果を取得できることを保証します。もちろん、このような戦略は、応答がない場合に呼び出し元が常にハングする原因となります。
4) NFS では、マウント時に soft/hard パラメーターを通じて SunRPC のリトライ戦略を指定できます。soft モードはタイムアウト後のリトライを禁止し、hard モードはリトライを続けます。ユーザーが soft モードでマウントすると、NFS 実装はクライアントとサーバーのステートビューの一貫性を保証しません。リモートプロシージャコールがタイムアウトを返す場合、アプリケーションはステートのクリーンアップと回復に協力する必要があります。たとえば、アクセスエラーが発生したファイルを閉じるなどです。ただし、実際には協調するアプリケーションはほとんどないため、一般的に NAS ユーザーは hard モードを使用してマウントします。
要するに、SunRPC が解決する必要がある核心的な問題の一つは、リモートプロシージャコールの実行時間が制御不能であることです。プロトコル設計者はこれに合わせて設計をカスタマイズし、非冪等 RPC リトライによって引き起こされる副作用を回避しようとしています。
シグナル割り込み
アプリケーションは、リモートプロシージャコールの結果を待機している間にシグナルによって割り込まれることが許可されています。シグナル割り込みが発生した場合、リモートプロシージャコールの実行結果が取得されていないため、クライアントとサーバーのステートが一致しない可能性があります。たとえば、ロック操作がサーバー側で正常に実行されていますが、クライアントはこの状況を知りません。これには、クライアントがステートをサーバーに復元するために追加の作業を行う必要があります。以下では、シグナル割り込み後にファイルロックを取得するプロセスを簡単に分析し、NFS プロトコル実装レベルの一貫性設計を説明します。
NFSv4 ファイルロックの取得プロセスを通じて、NFSv4 は最終的に _nfs4_do_setlk() 関数を呼び出して RPC 操作を開始し、最後に nfs4_wait_for_completion_rpc_task() を呼び出して待機することがわかります。以下が関連コードです。
5684 static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type)
5685 {
 …
5718 task = rpc_run_task(&task_setup_data);
5719 if (IS_ERR(task))
5720 return PTR_ERR(task);
5721 ret = nfs4_wait_for_completion_rpc_task(task);
5722 if (ret == 0) {
5723 ret = data->rpc_status;
5724 if (ret)
5725 nfs4_handle_setlk_error(data->server, data->lsp,
5726 data->arg.new_lock_owner, ret);
5727 } else
5728 data->cancelled = 1;
 …
}
Copy
nfs4_wait_for_completion_rpc_task() の実装を分析すると、ret < 0 の場合、ロック取得プロセスがシグナル割り込みを受けたことを示し、struct nfs4_lockdata の cancelled メンバーが使用されることがわかります。完了後に rpc_task が解放されるときのコールバック関数 nfs4_lock_release() を引き続き確認します。
上の赤いボックスのコードからわかるように、nfs4_lock_release() がシグナル割り込みを検出すると、nfs4_do_unlck() 関数を呼び出して、正常に取得された可能性のあるファイルロックを解放しようとします。この時点で nfs_free_seqid() 関数が呼び出されないのは、保持されている nfs_seqid を解放しないためです。これは以下の理由によるものです。
1) ステートを修正するプロセス中に、ユーザーによって開始される並行ロックまたは解放操作がないことを保証し、実装を簡素化します。
2) hard モードでの UNLOCK 操作が LOCK 操作の戻り後にのみ送信されることを保証し、取得したロックを解放できるようにします。
上記の方法により、クライアントはシグナル割り込み後のクライアントとサーバーのロックステータスの最終的な一貫性を効果的に保証できます。ただし、これは可用性の一部を犠牲にするコストも伴います。
まとめ
ファイルロックは、ファイルシステムがネイティブにサポートする基本機能です。共有ファイルシステムである NAS は、クライアントとサーバー間のロックステータスビューの一貫性の問題に対処する必要があります。NFSv4.0 はこの問題をある程度解決しました。もちろん、技術進歩のペースが止まることはなく、NFS の更新も止まることはありません。将来、NFS に対するさらなる期待があるでしょう。
ファイルロックは、ファイルシステムの最も基本的な機能の一つです。ファイルロックを利用することで、アプリケーションは他のアプリケーションからのファイルへの同時アクセスを制御できます。UNIX 系システムの標準ネットワークファイルシステムである NFS は、開発プロセスの中で徐々にネイティブにファイルロックをサポートしてきました(NFSv4 以降)。NFS は 1980 年代の誕生以来、NFSv2、NFSv3、NFSv4 の 3 つのバージョンをリリースしてきました。NFSv4 の最大の変更点は「ステート」です。特定の操作では、サーバーが関連するステートを維持する必要があります。たとえば、ファイルロックです。クライアントがファイルロックを申請すると、サーバーはファイルロックのステートを維持する必要があります。そうしないと、他のクライアントからの競合するアクセスを検出できません。NFSv3 の場合、ファイルロック機能を実現するには NLM の支援が必要ですが、両者がうまく連携できない場合があり、エラーが発生しやすくなります。一方、NFSv4 はステートフルプロトコルとして設計されており、単独でファイルロック機能を実現できるため、NLM プロトコルは不要です。
アプリケーションインターフェイス
アプリケーションは、fcntl() または flock() システムコールを通じて NFS ファイルロックを管理できます。以下は、NFSv4 を使用して NAS をマウントする際のファイルロック取得の呼び出しプロセスです。
上の図の呼び出しスタックから、NFS ファイルロックの実装ロジックが基本的に VFS レイヤーの設計とデータ構造を再利用していることが容易にわかります。RPC を通じてサーバーからファイルロックを正常に取得した後、locks_lock_inode_wait() 関数が呼び出され、取得したファイルロックが VFS レイヤーに転送されて管理されます。VFS レイヤーのファイルロック設計に関する資料は多数存在するため、ここでの詳細な説明は省略します。
EOS の原理
ファイルロックは典型的な非冪等操作です。ファイルロック操作のリトライとフェールオーバーは、クライアントとサーバー間でファイルロックステータスビューの不整合を引き起こします。NFSv4 は、最大 1 回のみ実行できるメカニズムを設計するために SeqId メカニズムを使用しています。具体的な方法は以下の通りです。
各 open/lock ステートに対して、クライアントとサーバーは独立して同時に seqid を維持します。クライアントがステート変更を引き起こす操作(open/close/lock/unlock/release_lockowner)を開始すると、seqid に 1 を加えてパラメーターとしてサーバーに送信します。クライアントが送信した seqid を R、サーバーが維持している seqid を L とすると、以下のようになります。
1) R == L +1 の場合、合法なリクエストであることを意味し、正常に処理する必要があります。
2) R == L の場合、リクエストのリトライであることを意味し、サーバーはキャッシュされた応答を返します。
3) その他の場合、不正なリクエストであり、アクセスは絶対に禁止されます。
上記のルールに従って、サーバーは操作が正常か、リトライか、不正なリクエストかを判断できます。
この方法により、各ファイルロック操作がサーバー側で最大 1 回のみ実行されることが保証され、RPC リトライによる繰り返し実行の問題が解決されます。しかし、これだけでは不十分です。たとえば、LOCK 操作の送信後、呼び出しスレッドがシグナルによって割り込まれ、その後サーバーが LOCK 操作を正常に受け入れて実行した場合、サーバーはクライアントがロックを保持していることを記録しますが、クライアントは割り込みのためにロックを維持しません。これにより、クライアントとサーバー間でロックステータスビューの不整合が発生します。したがって、クライアントも異常なシナリオの処理に協力する必要があり、最終的にファイルロックビューの一貫性を達成します。
例外処理
前のセクションの分析から、クライアントがファイルビューの一貫性を確保するために異常なシナリオの処理に協力する必要があることがわかりました。では、クライアント設計者は主にどのような協調設計を行ったのでしょうか。現在、クライアントは主に SunRPC と NFS プロトコルの 2 つの側面の相互協力によってこの問題を解決しています。以下では、これら 2 つの側面の設計がどのようにファイルロックステータスビューの一貫性を確保しているかを紹介します。
SunRPC 設計
SunRPC は、Sun がリモートプロシージャコール専用に設計したネットワーク通信プロトコルです。ここでは、ファイルロックビューの一貫性を確保するという側面から、SunRPC 実装レベルの設計概念を理解します。
1) クライアントは int32 型 xid を使用して、上位ユーザーによって開始された各リモートプロシージャコールプロセスを識別します。各リモートプロシージャコールの複数の RPC リトライは同じ xid 識別子を使用し、これにより、いずれかの応答が上位層にリモートプロシージャコールが成功したことを通知でき、サーバーがリモートプロシージャコールの実行に長時間かかっても結果を取得できることを保証します。これは従来の netty/mina/brpc などと同じです。各 RPC には独立した xid/packetid が必要です。
2) サーバーは DRC(Duplicate Request Cache)を設計し、最近実行された RPC 結果をキャッシュします。RPC を受信すると、まず xid を通じて DRC キャッシュを検索します。ヒットした場合、RPC がリトライ操作であることを示し、キャッシュされた結果を直接返すことができます。これにより、RPC リトライによる繰り返し実行の問題がある程度回避されます。xid の再利用によって DRC キャッシュが予期しない結果を返すのを避けるために、開発者は以下の設計を通じて再利用によるエラーの発生確率をさらに効果的に低減しています。
a) クライアントが新しい接続を確立する際、初期 xid はランダム値を採用します。
b) サーバー側の DRC はリクエストの検証情報を追加で記録し、キャッシュヒット時に同時に検証されます。
3) クライアントはサーバーからの応答を取得する前に無制限にリトライを許可され、呼び出し元がサーバーの確定的な実行結果を取得できることを保証します。もちろん、このような戦略は、応答がない場合に呼び出し元が常にハングする原因となります。
4) NFS では、マウント時に soft/hard パラメーターを通じて SunRPC のリトライ戦略を指定できます。soft モードはタイムアウト後のリトライを禁止し、hard モードはリトライを続けます。ユーザーが soft モードでマウントすると、NFS 実装はクライアントとサーバーのステートビューの一貫性を保証しません。リモートプロシージャコールがタイムアウトを返す場合、アプリケーションはステートのクリーンアップと回復に協力する必要があります。たとえば、アクセスエラーが発生したファイルを閉じるなどです。ただし、実際には協調するアプリケーションはほとんどないため、一般的に NAS ユーザーは hard モードを使用してマウントします。
要するに、SunRPC が解決する必要がある核心的な問題の一つは、リモートプロシージャコールの実行時間が制御不能であることです。プロトコル設計者はこれに合わせて設計をカスタマイズし、非冪等 RPC リトライによって引き起こされる副作用を回避しようとしています。
シグナル割り込み
アプリケーションは、リモートプロシージャコールの結果を待機している間にシグナルによって割り込まれることが許可されています。シグナル割り込みが発生した場合、リモートプロシージャコールの実行結果が取得されていないため、クライアントとサーバーのステートが一致しない可能性があります。たとえば、ロック操作がサーバー側で正常に実行されていますが、クライアントはこの状況を知りません。これには、クライアントがステートをサーバーに復元するために追加の作業を行う必要があります。以下では、シグナル割り込み後にファイルロックを取得するプロセスを簡単に分析し、NFS プロトコル実装レベルの一貫性設計を説明します。
NFSv4 ファイルロックの取得プロセスを通じて、NFSv4 は最終的に _nfs4_do_setlk() 関数を呼び出して RPC 操作を開始し、最後に nfs4_wait_for_completion_rpc_task() を呼び出して待機することがわかります。以下が関連コードです。
5684 static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type)
5685 {
 …
5718 task = rpc_run_task(&task_setup_data);
5719 if (IS_ERR(task))
5720 return PTR_ERR(task);
5721 ret = nfs4_wait_for_completion_rpc_task(task);
5722 if (ret == 0) {
5723 ret = data->rpc_status;
5724 if (ret)
5725 nfs4_handle_setlk_error(data->server, data->lsp,
5726 data->arg.new_lock_owner, ret);
5727 } else
5728 data->cancelled = 1;
 …
}
Copy
nfs4_wait_for_completion_rpc_task() の実装を分析すると、ret < 0 の場合、ロック取得プロセスがシグナル割り込みを受けたことを示し、struct nfs4_lockdata の cancelled メンバーが使用されることがわかります。完了後に rpc_task が解放されるときのコールバック関数 nfs4_lock_release() を引き続き確認します。
上の赤いボックスのコードからわかるように、nfs4_lock_release() がシグナル割り込みを検出すると、nfs4_do_unlck() 関数を呼び出して、正常に取得された可能性のあるファイルロックを解放しようとします。この時点で nfs_free_seqid() 関数が呼び出されないのは、保持されている nfs_seqid を解放しないためです。これは以下の理由によるものです。
1) ステートを修正するプロセス中に、ユーザーによって開始される並行ロックまたは解放操作がないことを保証し、実装を簡素化します。
2) hard モードでの UNLOCK 操作が LOCK 操作の戻り後にのみ送信されることを保証し、取得したロックを解放できるようにします。
上記の方法により、クライアントはシグナル割り込み後のクライアントとサーバーのロックステータスの最終的な一貫性を効果的に保証できます。ただし、これは可用性の一部を犠牲にするコストも伴います。
まとめ
ファイルロックは、ファイルシステムがネイティブにサポートする基本機能です。共有ファイルシステムである NAS は、クライアントとサーバー間のロックステータスビューの一貫性の問題に対処する必要があります。NFSv4.0 はこの問題をある程度解決しました。もちろん、技術進歩のペースが止まることはなく、NFS の更新も止まることはありません。将来、NFS に対するさらなる期待があるでしょう。
Related Articles
-
A detailed explanation of Hadoop core architecture HDFS
Knowledge Base Team
-
What Does IOT Mean
Knowledge Base Team
-
6 Optional Technologies for Data Storage
Knowledge Base Team
-
What Is Blockchain Technology
Knowledge Base Team
Explore More Special Offers
-
Short Message Service(SMS) & Mail Service
50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00
