ZooKeeper のlock のレシピを読んでいて、混乱しました。分散ロックのこのレシピは、「2 つのクライアントが同じロックを保持しているとは思わない時間内のスナップショット」を保証できないようです。しかし、ZooKeeper は非常に広く採用されているため、参考文献にそのような間違いがある場合は、誰かがずっと前に指摘しているはずですが、何を誤解したのでしょうか?
分散ロックのレシピを引用する:
ロック
グローバルに同期された完全に分散されたロック。つまり、任意のスナップショットで、2 つのクライアントが同じロックを保持しているとは見なしません。これらは ZooKeeper を使用して実装できます。プライオリティ キューと同様に、最初にロック ノードを定義します。
- " locknode /guid-lock- " というパス名を指定して create( ) を呼び出し、シーケンス フラグとエフェメラル フラグを設定します。
- ウォッチ フラグを設定せずにロック ノードで getChildren( ) を呼び出します (これは、群れ効果を回避するために重要です)。
- 手順 1 で作成されたパス名に最小のシーケンス番号サフィックスが付いている場合、クライアントはロックされ、クライアントはプロトコルを終了します。
- クライアントは、次に小さいシーケンス番号を持つロック ディレクトリ内のパスに監視フラグを設定して exists( ) を呼び出します。
- exists( ) が false を返す場合は、ステップ 2 に進みます。それ以外の場合は、ステップ 2 に進む前に、前のステップからのパス名の通知を待ちます。
次のケースを検討してください。
- Client1 は、ZooKeeper ノード "locknode/guid-lock-0" を使用して (手順 3 で) ロックを正常に取得しました。
- Client2 はノード「locknode/guid-lock-1」を作成しましたが、ロックの取得に失敗し、現在「locknode/guid-lock-0」を監視しています。
- その後、何らかの理由 (たとえば、ネットワークの輻輳) で、Client1 は時間どおりに ZooKeeper クラスターにハートビート メッセージを送信できませんでしたが、Client1 はまだロックを保持していると誤って想定して、別の場所で作業を続けています。
ただし、ZooKeeper は Client1 のセッションがタイムアウトしたと判断し、
- 「locknode/guid-lock-0」を削除し、
- Client2 に通知を送信します (または、最初に通知を送信しますか?)、
- しかし、「セッション タイムアウト」通知を Client1 に時間内に送信できません (たとえば、ネットワークの輻輳のため)。
- Client2 は通知を受け取り、ステップ 2 に進み、自分で作成した唯一のノード「locknode/guid-lock-1」を取得します。したがって、Client2 はロックを保持していると想定します。
- しかし同時に、Client1 はロックを保持していると想定します。
これは有効なシナリオですか?