プログラミング
ステート指向の一貫性:単一の正解を探すのをやめた理由
State-oriented consistency: Why we stopped looking for one right answer (keel-iot.eu)
要約
分散システムにおいて、システム全体で単一の一貫性モデルを適用しようとする「均一一貫性」という設計上の誤りを指摘しています。筆者らは、個々のステートが持つ固有の要件を理解し、それぞれに最適な一貫性保証を選択する「ステート指向一貫性」というアプローチを提唱しています。このアプローチにより、システムの設計が簡素化され、スケーラビリティの問題を回避できるとしています。
全文翻訳
あらゆる分散システムには、さまざまな種類の一貫性があります。私たちが初期に犯した間違いは、間違った質問をしていたことです。「クラスターはどの整合性モデルを使用すべきか?」と問い続けていました。しかし、有用な質問は「この特定のステートの断片は、実際にはどのような整合性保証を必要としているのか?」であることが判明しました。これらは似ているように聞こえますが、そうではありません。前者はシステム全体に単一の答えが存在すると仮定します。後者はそうではないと仮定します。そして、この全体が収束するのは、最終的にほとんどの説明をしてくれる一つの表の形になります。私たちは、クラスタリングされたメッセージブローカーを構築する際に、この違いを苦労して学びました。その根底にある教訓は、MQTTとはほとんど関係がありません。複数の種類のステートを保持するあらゆる分散システムは、最終的にこの問題に直面します。これを正しく理解すれば、残りの設計はほぼ自動的に単純化されます。
インシデント
カーネルのOOMハンドラによって、2つのポッドが5分間隔で停止しました。メモリ制限は512Miでした。特に変わったことはなく、通常のステートレスサービスに対する通常のコンテナ制限でした。最初の仮説は明白なものでした。ロードバランサーの不均衡が、おそらく再接続の連鎖に複合的に影響したというものです。もっともらしい。しかし、間違っていました。
同じウィンドウでのアクティブ接続、3つのポッド:
ポッドA:8 ██
ポッドB:174 ████████████████
ポッドC:1,039 ████████████████████████████████████████
同じ3つのポッド、同じウィンドウでのワーキングセットメモリ:
ポッドA:約270MB ████████████████████████████
ポッドB:約310MB ████████████████████████████████
ポッドC:約360MB █████████████████████████████████████
これは、形状の不均衡が生成するものではありません。1つのポッドが他のポッドの130倍の接続を処理している場合でも、メモリ使用量がほぼ同じであるはずがありません。メトリックが嘘をついているか、メンタルモデルが間違っていたかのどちらかです。
調査
メトリックは嘘をついていませんでした。セッションステートを担当する実際のコードパスを読むと、本当の原因が明らかになりました。各ポッドの起動時に、永続化フックがすべてのクライアントの保存されたセッションステートをロードしていました。これは、その特定のポッドに再接続する可能性のあるクライアントの割合だけでなく、フリート全体のものでした。フィルタリングされていない1回の読み込みが起動時に一度だけ呼び出され、返されたすべての行がメモリ上のライブオブジェクトになりました。
なぜ誰かがそのように書いたのでしょうか?ステートレスなクラスターで、非スティッキーなロードバランサーの後ろにある場合、どのクライアントがこのポッドに再接続するかを事前に知る方法は実際にはありません。そのため、最も単純で正しそうに見える実装は、「すべてをどこでもロードし、接続するものがそのステートを見つけられるようにする」というものでした。これはタイプミスバグではなく、ノードは現れる可能性のあるあらゆるクライアントにサービスを提供する準備ができているべきだという設計上の静かな仮定でした。冒頭の質問と全く同じ、小さく局所的な間違いでした。「クラスターは誰にでもサービスを提供できる」ことを最適化しましたが、「この特定のステートにはこの特定の要件がある」ということは最適化しませんでした。
この連鎖がアーキテクチャの問題として現れることはめったにありません。間違った抽象化 ↓ 間違った保証 ↓ 間違ったアーキテクチャ ↓ 間違ったスケーリング
間違った抽象化:どのノードがこのクライアントにサービスを提供するかを、1つだけではなく、すべてのノードが回答を必要とするかのように扱った。
間違った保証:万が一のために、すべてに複製した。
間違ったアーキテクチャ:ノードが責任を持つ範囲に境界がなかった。
間違ったスケーリング:コストがフリートサイズではなく、実際の責任に応じて増加した。
OOMは、この連鎖が可視化された場所でしかありませんでした。それは、誰も説明できなかったスケーリングの限界、またはフリートが成長するにつれて不思議とリスクが高まったローリングアップデートとして現れた可能性もあります。
この問題が提起する質問
率直に言えば、これはメモリバグではなく、モデリングエラーのように見え始めます。私たちは、このステートがどこにでも複製される必要があると仮定していました。実際には、それは全く必要ありませんでした。それは、どのノードでも計算できるルールによって決定される、ただ一つのオーナーが必要なだけでした。この単一のインシデントを超えて生き残る質問は、「どうすればメモリ使用量を減らせるか」ではなく、「このステートは実際には何を必要としており、私たちはそれ以上に与えていなかったか?」です。
修正する前に間違いを名前で呼ぶ
私たちが陥ったデフォルトにも、意図的に選んだわけではありませんが、名前が必要です。私たちはこの設計習慣を「均一一貫性」と呼ぶようになりました。システム全体に単一の一貫性戦略を適用し、その特定のステートが必要とするものに関係なく、すべてのステートがそれを継承するようにします。明確にしておきますが、これは特定のアルゴリズムを意味するものではありません。「均一一貫性」は論文で見つかるようなテクニックではありません。これは、各ステートが実際にそれを必要としているかどうかを尋ねることなく、システムがデフォルトで信頼している一貫性モデルをどこでも利用するという習慣を意味します。
間違った仮定
現実
────────────────
───────
分散システムのすべてのステートは、同じ保証を必要とする。
各ステートは、それぞれ独自のセマンティクス、独自の答えを必要とする。
───────►「2つのノードが一時的に意見の相違をした場合、どうなるか?」
誰も「均一一貫性を適用する」と決めてシステムを設計するわけではありません。それは、最初の質問が未解決のまま、一つ一つの小さな決定によって、デフォルトで起こることです。これはストローマンではありません。私たちが実際にやっていたことであり、それを長く続けるとコストがかかるのがOOMです。
ステートの分類
アウトエージを引き起こしたステートだけでなく、クラスターのすべてのステートに対して「このステートは実際には何を必要としているか?」と尋ねることが、表の由来です。この部分の他のすべては、実際には一つのリフレームを適用しているだけです。一貫性はシステムのプロパティではありません。それは個々のステートのプロパティです。
ステート
もし2つのノードが一時的に意見の相違をした場合...
最小限必要な保証
ライブ接続の所有権
重複配信、または誰も運転していないゴースト接続
単一の権威あるオーナー、今すぐ決定
オフラインクライアントの所有権
何も悪いことはない - 重複するライブ接続はない
決定論的 - 計算可能、仲裁不要
メッセージルーティング
一時的に古いルート、すぐに自己修正
利用可能 - 一時的な遅延に耐性がある
永続メッセージステート
静かで、永続的なデータ損失
永続的 - 例外なし
クラスターメンバーシップ
ノードが一時的に生きているように見えるが、実際にはそうではない
最終的に一貫性がある
5行、5つの異なる最小保証。洞察は単一の行ではありません。それは、有能な人々によって構築されたシステムでも、何かがこの質問を表に、行ごとに強制しない限り、均一一貫性にデフォルト設定されるということです。
フレームワーク
これは、MQTT、分散ブローカー、さらにはこのプロジェクト全体を超えて一般化できると考えている部分です。
1. ステートを特定する。
│ ▼
2. 2つのノードが意見の相違をした場合に何が起こるかを尋ねる。
│ ▼
3. 実際に必要な最小限の保証を見つける。
│ ▼
4. その保証を提供する最も弱いメカニズムを選択する。
│ ▼
5. 次のステートのために繰り返す。
正確な答えはシステムによって異なります。スケジューラ、データベース、サービスメッシュ、MQTTブローカーは、同じステートを同じようには分類しません。スケジューラの「タスクの所有者は誰か」は、ブローカーの「セッションの所有者は誰か」とは異なり、そう強制されるべきではありません。ループは同じままです。表の行だけが変わります。ポイントは、異なるシステムが同じ答えにたどり着くことではありません。ポイントは、それらが同じ推論プロセスを使用してそれらにたどり着くことができるということです。
私たちはこの設計ルールを内部的に「ステート指向一貫性」と呼ぶようになりました。これは新しい分散システム理論だからではなく、アイデアに名前を付けることでプロジェクト全体で一貫して適用しやすくなり、習慣から均一一貫性に手を伸ばしたときに自分たちを捕まえやすくなったからです。私たちはこのアイデアが新しいと主張するつもりはありません。名前を付けることで、より一貫して推論しやすくなることがわかりました。このようにステートを分類し始めると、アーキテクチャはほとんど自己設計されました。各行は、それぞれ異なる保証を必要としたため、自然に異なるメカニズムを示唆しました。
私たちが実際に構築したもの
セマンティクスが保証を決定する。保証がメカニズムを決定する。メカニズムがアーキテクチャを形成する。その表は、行ごとに1つのメカニズムに翻訳され、それぞれが独立していました。そして、保証がメカニズムを決定するのであって、その逆ではないため、マッピングは2列ではなく3列として読む場合にのみ意味があります。
ステート
保証
メカニズム