ノード間メッセージングの改善
Apache Cassandra 4.0では、ノード間メッセージングにいくつかの改善が追加されました。
最適化されたノード間メッセージングプロトコル
ノード間メッセージングプロトコルが最適化されました(CASSANDRA-14485)。以前は、初期接続/セッション確立時に`IPAddressAndPort`が一度送信されたにもかかわらず、送信される各メッセージに送信者の`IPAddressAndPort`が含まれていました。Cassandra 4.0では、`IPAddressAndPort`は送信される個々のメッセージから削除され、接続/セッションの開始時のみ送信されるようになりました。
もう1つの改善点として、いくつかのインスタンス(リストされている)で、固定の4バイト整数値が`vint`に置き換えられました。`vint`はほぼ常に1バイト未満であるためです。
-
`paramSize`(ヘッダーのパラメータ数)
-
個々のパラメータ値
-
`payloadSize`
NIOメッセージング
Cassandra 4.0では、ピアツーピア(ノード間)メッセージングが、Nettyを使用したノンブロッキングI/O(NIO)に切り替えられました(CASSANDRA-8457)。
シリアル化形式として、各メッセージには、いくつかの固定フィールドを持つヘッダー、オプションのキーバリューパラメータセクション、メッセージペイロード自体が含まれています。注:ヘッダーのIPアドレスは、IPv4(4バイト)またはIPv6(16バイト)のいずれかです。
簡潔にするため、以下の図ではIPv4アドレスを示しています。
1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 6 6 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | PROTOCOL MAGIC | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Message ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Addr len | IP Address (IPv4) / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / | Verb / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / | Parameters size / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / | Parameter data / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Payload size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / Payload / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
個々のパラメータには、String型のキーとバイト配列型の値があります。キーは、その長さ(2バイトでエンコード)を先頭に付け、文字列のUTF-8バイトエンコーディングが続きます。本文は、その長さ(4バイトでエンコード)を先頭に付け、値のバイトが続きます。
キューに入れられたメッセージのリソース制限
メッセージの`serializedSize`で測定される、キューに入れられた送信メッセージの数に厳格なリソース制限(CASSANDRA-15066)を適用することで、システムの安定性が向上しました。ノードの安定性に影響を与える可能性のある失敗の妥当な組み合わせがない限り、常に進捗状況が確保されるように、3つの個別の制限が同時に適用されます。
-
グローバル、エンドポイントごと、接続ごとの制限が、他のノードへの配信を待機しているメッセージ、およびクラスタ内の他のノードからの到着を待機しているメッセージに適用されます。これらの制限は、送受信されるメッセージのオンワイヤサイズに適用されます。
-
基本的なリンクごとの制限は、エンドポイントまたはグローバル制限が適用される前に、個別に消費されます。各ノードペアには、緊急、小規模、大規模の3つのリンクがあります。任意のノードでは、それらの間で調整を行わなくても、最大で`N*3 * (internode_application_send_queue_capacity in bytes + internode_application_receive_queue_capacity in bytes)`のメッセージデータがキューに入れられる可能性があります。ただし、実際には、トークン認識ルーティングを使用することで、RF*トークンのノードのみが、かなりの帯域幅で通信する必要があります。
-
エンドポイントごとの制限は、リンクごとの制限を超えるすべてのメッセージに、グローバル制限と同時に適用されます。グローバル制限は、リンクごとの制限を超えるすべてのメッセージに、エンドポイントごとの制限と同時に適用されます。キューに入れられたメッセージのリソース制限については、`cassandra.yaml`に以下の設定が追加されました。
internode_application_send_queue_capacity: 4MiB internode_application_send_queue_reserve_endpoint_capacity: 128MiB internode_application_send_queue_reserve_global_capacity: 512MiB internode_application_receive_queue_capacity: 4MiB internode_application_receive_queue_reserve_endpoint_capacity: 128MiB internode_application_receive_queue_reserve_global_capacity: 512MiB
メッセージングメトリクスの仮想テーブル
ノード間の送受信メッセージングのメトリクスを仮想テーブルを使用して保持することで、メトリクスが改善されました(CASSANDRA-15066)。受信メッセージングについては、次のメトリクスを保持するために仮想テーブル(`internode_inbound`)が追加されました。
-
エラーのためシリアル化またはフラッシュできなかったメッセージのバイト数と件数
-
スケジュールされたメッセージのバイト数と件数
-
正常に処理されたメッセージのバイト数と件数
-
正常に受信されたメッセージのバイト数と件数
-
スロットルされたメッセージのナノ秒数と件数
-
期限切れのメッセージのバイト数と件数
-
復旧された破損フレームと復旧されなかった破損フレーム
ノード間の送信メッセージングについては、別の仮想テーブル(`internode_outbound`)が追加されました。送信仮想テーブルは、次のメトリクスを保持します。
-
保留中のメッセージのバイト数と件数
-
送信されたメッセージのバイト数と件数
-
期限切れのメッセージのバイト数と件数
-
エラーのため送信できなかったメッセージのバイト数と件数
-
過負荷のメッセージのバイト数と件数
-
アクティブな接続数
-
接続試行回数
-
成功した接続試行回数
ヒントメッセージング
`ByteBuffer`に既にエンコードされているヒントを受け取り、そのまま送信するヒントメッセージの特殊バージョンが追加されました。これは、現在のメッセージングバージョンのヒントファイルを同じメッセージングバージョンのノードにディスパッチする場合(最も一般的なケース)の最適化です。追加の`ByteBuffer`の割り当てと冗長なヒントの逆シリアル化-シリアル化サイクルを節約できます。
ノード間アプリケーションタイムアウト
アプリケーション空間で接続が書き込み不能になる最大連続期間の設定が`cassandra.yaml`に追加されました。
# internode_application_timeout_in_ms = 30000
その他の新機能には、クエリのトレースのためにメッセージサイズのログ記録が含まれています。
ローカルリクエストに対するPaxos prepareとproposeステージの最適化
4.0以前のバージョンでは、Paxosのprepareメッセージとproposeメッセージは、リクエストがローカルで処理される場合でも、Cassandraの全`MessagingService`スタックを経由していました。ローカルリクエストを`MessagingService`を介さずに処理することで、機能強化を実現できます。Cassandraの他の場所でも同様の処理が行われており、ローカルリクエストに対して`MessagingService`ステージをスキップしています。
トレースを有効にして軽量トランザクションを実行した場合、4.0以前のバージョンでは次のようになります。
Sending PAXOS_PREPARE message to /A.B.C.D [MessagingService-Outgoing-/A.B.C.D] | 2017-09-11 21:55:18.971000 | A.B.C.D | 15045 … REQUEST_RESPONSE message received from /A.B.C.D [MessagingService-Incoming-/A.B.C.D] | 2017-09-11 21:55:18.976000 | A.B.C.D | 20270 … Processing response from /A.B.C.D [SharedPool-Worker-4] | 2017-09-11 21:55:18.976000 | A.B.C.D | 20372
Proposeステージにも同様のことが当てはまります。
バージョン4.0では、ローカルリクエストに対するPaxos prepareとproposeステージが最適化されました(CASSANDRA-13862)。
品質保証
バージョン4.0では、いくつかの品質保証の改善が行われました(CASSANDRA-15066)。
フレーミング
バージョン4.0では、すべてのノード間メッセージにフレーミングが導入されました。つまり、メッセージをヘッダーとトレーラーを持つ単一の論理ペイロードにグループ化します。これらのフレームには、最大で1つのメッセージ(大きなメッセージの場合は独自のシーケンスに分割される)が含まれるか、フレームには完全なメッセージのみが含まれることが保証されています。
破損防止
以前は、データセンター内のノード間メッセージは、LZ4のみが整合性チェックを提供していたため、デフォルトで破損から保護されていませんでした。4.0以降のノードへのすべてのメッセージは、明示的なフレームに書き込まれます。フレームは次のいずれかです。
-
LZ4エンコード
-
CRC保護
無保護オプションも使用できます。
耐障害性
耐障害性のために、すべてのフレームはそれぞれ8バイトと6バイトのCRC保護ヘッダー付きで書き込まれます。このヘッダーに破損が発生した場合、以前と同様に接続をリセットする必要があります。ヘッダー以外の場所で破損が発生した場合、破損したフレームはスキップされ、接続は維持され、メッセージの不要な損失を回避します。
以前は、ストリームの任意の場所で問題が発生すると、接続がリセットされ、転送中のメッセージが失われていました。
効率性
着信メッセージと発信メッセージの両方における、全体的なメモリ使用量とバイトシャッフルの回数が削減されました。
発信側では、Netty LZ4エンコーダは、圧縮フレームを生成する前に満たされるチャンクサイズバッファ(64KiB)を維持します。私たちのフレームエンコーダは、この冗長なコピーを回避し、エンドポイントごとに192KiBを解放します。
着信側では、フレームデコーダは、フレームを解析するために必要なバイト数だけをコピーし、必要以上のバイトを保存しないことを保証します。この改善はLZ4接続に2回適用され、メッセージのデコードとLZ4フレームのデコードの両方が改善されます。
着信パス
バージョン4.0では、着信パスにいくつかの改善が導入されました。
フラグで設定されているように、特定の接続で大小どちらのメッセージが期待されるかに基づいて、適切なメッセージハンドラが使用されます。小さなメッセージにはイベントループ上で動作するNonblockingBufferHandler
が、大きなメッセージにはイベントループ外で動作するBlockingBufferHandler
が使用されます。InboundMessageHandler
の単一の実装は、バイトストリームから着信メッセージのサイズを導出することで、あらゆるサイズのメッセージを効果的に処理します。ストリームからメッセージのサイズを導出することに加えて、メッセージ全体の逆シリアル化を試行する前に、着信メッセージの有効期限が事前に読み取られます。メッセージが見つかった時点で期限切れになっている場合、メッセージはバイトストリーム全体でスキップされます。そして、受信側でまだメッセージの逆シリアル化が失敗した場合(たとえば、テーブルIDまたは列が不明なため)、接続全体を切断してバッファリングされたすべてのメッセージを失うことなく、バイトがスキップされます。コーディネータのコールバックが期限切れになるのを待つのではなく、失敗の理由とともに、コーディネータノードにすぐに返信が送られます。このロジックは破損したフレームにも拡張され、破損したフレームは接続を切断することなく安全にスキップされます。
着信パスは、メモリ使用量に厳格な制限を課します。具体的には、解析済みだが未処理のメッセージが占めるメモリは、接続ごと、エンドポイントごと、グローバルな基準で制限されます。接続がローカルの未処理容量を超え、エンドポイントごとのグローバルリザーブから許可を借りることができなくなると、十分な容量が回復するまで、それ以上のメッセージの処理を単純に停止し、自然なバックプレッシャーを提供します。
発信接続
接続の開始
エンドポイントによる拒否、互換性のないバージョン、予期しない例外など、あらゆる種類の接続失敗に対して一貫したアプローチが採用されています。
-
成功するか、配信待ちのメッセージがなくなるまで、永遠に再試行します。
-
再接続する前に、最大1秒まで徐々に長い時間待ちます。
-
接続に失敗している間は、予約キューの制限は取得されません。
接続の切断
-
配信待ちの発信メッセージを正しくドレインします(切断され、再接続に失敗した場合を除く)。
-
切断中の接続に書き込まれたメッセージは、配信されるか拒否されます。古い接続が取り消し不能に閉じられた場合は、新しい接続が開かれます。
-
未使用の接続は最終的に削除されます。
メッセージサイズ制限の追加
Cassandra 4.0以前は、ノード間メッセージオブジェクトに対して巨大なバッファを割り当てることからサーバーを保護していません。メッセージサイズ制限を追加すると、誤動作しているクラスタ参加者などの問題に対処するのに役立ちます。バージョン4.0では、最大変更サイズと同様の最大メッセージサイズ設定パラメータが導入され、デフォルトでエンドポイント予約容量に設定されています。
ノード間メッセージの逆シリアル化時の不明なテーブルからの復旧
(CASSANDRA-9289)で説明されているように、別のノードからのメッセージで不明なテーブルを検出した場合に正常に復旧できると便利です。4.0以前は、接続を閉じ、再接続していましたが、これにより、他の同時実行クエリが失敗する可能性があります。バージョン4.0では、メッセージインストリームをTrackedDataInputPlus
でラップし、UnknownCFException
をキャッチし、このメッセージの残りのバイトをスキップすることで、この問題を修正しました。TCPは閉じられず、他のメッセージのために接続されたままになります。