メインコンテンツまでスキップ

7. 輻輳制御 (Congestion Control)

本文書は、TCP NewReno [RFC6582] に類似した QUIC の送信側輻輳制御器を規定します。

QUIC が輻輳制御のために提供するシグナルは汎用的であり、異なる送信側アルゴリズムをサポートするように設計されています。送信者は、CUBIC [RFC8312] などの異なるアルゴリズムを一方的に選択できます。

送信者が本文書で指定されているものとは異なる制御器を使用する場合、選択された制御器は [RFC8085] のセクション 3.1 で指定されている輻輳制御ガイドラインに準拠しなければなりません (MUST)。

TCP と同様に、ACK フレームのみを含むパケットは送信中バイト (bytes in flight) にカウントされず、輻輳制御されません。TCP とは異なり、QUIC はこれらのパケットの損失を検出でき、その情報を使用して輻輳制御器または ACK のみのパケットの送信レートを調整できます (MAY) が、本文書ではそのためのメカニズムを説明していません。

輻輳制御器はパスごと (per path) であるため、他のパスで送信されたパケットは現在のパスの輻輳制御器を変更しません。[QUIC-TRANSPORT] のセクション 9.4 で説明されています。

本文書のアルゴリズムは、制御器の輻輳ウィンドウをバイト単位で指定および使用します。

エンドポイントは、bytes_in_flight(付録 B.2 参照)が輻輳ウィンドウより大きくなる原因となるパケットを送信してはなりません (MUST NOT)。ただし、そのパケットが PTO タイマーの期限切れ時に送信される場合(セクション 6.2 参照)または回復期に入る場合(セクション 7.3.2 参照)は除きます。


7.1. 明示的輻輳通知 (Explicit Congestion Notification)

パスが明示的輻輳通知 (ECN) [RFC3168] [RFC8311] をサポートするように検証されている場合、QUIC は IP ヘッダーの輻輳経験 (Congestion Experienced, CE) コードポイントを輻輳のシグナルとして扱います。本文書は、ピアが報告する ECN-CE カウントが増加したときのエンドポイントの応答を規定します。[QUIC-TRANSPORT] のセクション 13.4.2 を参照してください。


7.2. 初期および最小輻輳ウィンドウ (Initial and Minimum Congestion Window)

QUIC は、輻輳ウィンドウを初期値に設定してスロースタート (slow start) で各接続を開始します。エンドポイントは、最大データグラムサイズ (max_datagram_size) の 10 倍を初期輻輳ウィンドウとして使用すべきであり (SHOULD)、ウィンドウを 14,720 バイトまたは最大データグラムサイズの 2 倍のうち大きい方に制限します。これは [RFC6928] の分析と推奨に従っており、TCP の 20 バイトのオーバーヘッドと比較して UDP の 8 バイトのオーバーヘッドが小さいことを考慮してバイト制限を増やしています。

接続中に最大データグラムサイズが変化する場合、初期輻輳ウィンドウは新しいサイズで再計算されるべきです (SHOULD)。ハンドシェイクを完了するために最大データグラムサイズが減少した場合、輻輳ウィンドウは新しい初期輻輳ウィンドウに設定されるべきです (SHOULD)。

クライアントのアドレスを検証する前に、サーバーは [QUIC-TRANSPORT] のセクション 8.1 で指定されている反増幅制限によってさらに制限される可能性があります。反増幅制限は輻輳ウィンドウが完全に利用されることを妨げ、したがって輻輳ウィンドウの増加を遅くする可能性がありますが、輻輳ウィンドウに直接影響を与えません。

最小輻輳ウィンドウは、損失、ピアが報告する ECN-CE カウントの増加、または持続的輻輳 (persistent congestion) に応答して輻輳ウィンドウが達成できる最小値です。推奨 (RECOMMENDED) 値は 2 * max_datagram_size です。


7.3. 輻輳制御状態 (Congestion Control States)

本文書で説明されている NewReno 輻輳制御器には、図 1 に示すように 3 つの異なる状態があります。

       新しいパスまたは        +------------+
持続的輻輳 |スロー |
(O)---------------------->|スタート |
+------------+
|
損失または
ECN-CE 増加
|
v
+------------+ 損失または +------------+
| 輻輳回避 | ECN-CE 増加 | 回復期 |
| |------------------>| |
+------------+ +------------+
^ |
| |
+----------------------------+
回復期中に送信された
パケットの確認応答

図 1:輻輳制御状態と遷移

これらの状態とそれらの間の遷移は、後続のセクションで説明されます。

7.3.1. スロースタート (Slow Start)

輻輳ウィンドウがスロースタート閾値を下回っている場合、NewReno 送信者はスロースタートにあります。スロースタート閾値は無限値に初期化されるため、送信者はスロースタートで開始します。

送信者がスロースタートにある間、輻輳ウィンドウは各確認応答が処理されるときに確認されたバイト数だけ増加します。これにより、輻輳ウィンドウが指数関数的に成長します。

パケットが失われたとき、またはそのピアが報告する ECN-CE カウントが増加したとき、送信者はスロースタートを終了し、回復期に入らなければなりません (MUST)。

輻輳ウィンドウがスロースタート閾値未満のときはいつでも、送信者はスロースタートに再び入ります。これは持続的輻輳が宣言された後にのみ発生します。

7.3.2. 回復 (Recovery)

NewReno 送信者は、パケットの損失を検出したとき、またはそのピアが報告する ECN-CE カウントが増加したときに回復期に入ります。すでに回復期にある送信者はそこにとどまり、再び入りません。

回復期に入ると、送信者は損失が検出されたときの輻輳ウィンドウ値の半分にスロースタート閾値を設定しなければなりません (MUST)。輻輳ウィンドウは、回復期を終了する前にスロースタート閾値の減少した値に設定しなければなりません (MUST)。

実装は、回復期に入るときに輻輳ウィンドウを直ちに減少させるか、比例レート削減 (Proportional Rate Reduction) [PRR] などの他のメカニズムを使用して輻輳ウィンドウをより段階的に減少させることができます (MAY)。輻輳ウィンドウが直ちに減少される場合、減少の前に単一のパケットを送信できます。これは、失われたパケット内のデータが再送信される場合に損失回復を高速化し、[RFC6675] のセクション 5 で説明されている TCP と類似しています。

回復期は、1 往復あたり 1 回の輻輳ウィンドウ削減に制限することを目的としています。したがって、回復期中は、輻輳ウィンドウは新しい損失または ECN-CE カウントの増加に応答して変化しません。

回復期中に送信されたパケットが確認されると、回復期が終了し、送信者は輻輳回避に入ります。これは、回復を開始した失われたセグメントが確認されたときに終了する TCP の回復の定義とわずかに異なります [RFC5681]。

7.3.3. 輻輳回避 (Congestion Avoidance)

輻輳ウィンドウがスロースタート閾値以上で、回復期にないときはいつでも、NewReno 送信者は輻輳回避にあります。

輻輳回避の送信者は、加法的増加乗法的減少 (Additive Increase Multiplicative Decrease, AIMD) アプローチを使用します。これは、確認された各輻輳ウィンドウに対して輻輳ウィンドウへの増加を最大 1 つの最大データグラムサイズに制限しなければなりません (MUST)。

パケットが失われたとき、またはそのピアが報告する ECN-CE カウントが増加したときに、送信者は輻輳回避を終了し、回復期に入ります。


7.4. 復号化不能パケットの損失の無視 (Ignoring Loss of Undecryptable Packets)

ハンドシェイク中、パケットが到着したときに一部のパケット保護鍵が利用できない場合があり、受信者はパケットをドロップすることを選択できます。特に、Initial パケットが到着するまで Handshake および 0-RTT パケットは処理できず、ハンドシェイクが完了するまで 1-RTT パケットは処理できません。エンドポイントは、ピアがそれらのパケットを処理するためのパケット保護鍵を持つ前に到着した可能性がある Handshake、0-RTT、および 1-RTT パケットの損失を無視できます (MAY)。エンドポイントは、特定のパケット番号空間で最も早く確認されたパケットの後に送信されたパケットの損失を無視してはなりません (MUST NOT)。


7.5. プローブタイムアウト (Probe Timeout)

プローブパケットは輻輳制御器によってブロックされてはなりません (MUST NOT)。ただし、送信者はこれらのパケットを追加で送信中としてカウントしなければなりません (MUST)。これらのパケットはパケット損失を確立せずにネットワーク負荷を追加するためです。プローブパケットの送信により、パケットの損失または配信を確立する確認応答を受信するまで、送信者の送信中バイトが輻輳ウィンドウを超える可能性があることに注意してください。


7.6. 持続的輻輳 (Persistent Congestion)

送信者が十分に長い期間にわたって送信されたすべてのパケットの損失を確立すると、ネットワークは持続的輻輳 (persistent congestion) を経験していると見なされます。

7.6.1. 持続時間 (Duration)

持続的輻輳持続時間は次のように計算されます:

(smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay) * kPersistentCongestionThreshold

セクション 6.2 の PTO 計算とは異なり、この持続時間には、損失が確立されたパケット番号空間に関係なく max_ack_delay が含まれます。

この持続時間により、送信者は、TCP が末尾損失プローブ (Tail Loss Probes) [RFC8985] および RTO [RFC5681] で行うように、PTO 期限切れへの応答を含め、持続的輻輳を確立する前にできるだけ多くのパケットを送信できます。

kPersistentCongestionThreshold の大きい値は、送信者がネットワークの持続的輻輳に対する応答性を低下させ、輻輳したネットワークへの積極的な送信をもたらす可能性があります。値が小さすぎると、送信者が不必要に持続的輻輳を宣言し、送信者のスループットが低下する可能性があります。

kPersistentCongestionThreshold の推奨 (RECOMMENDED) 値は 3 であり、これにより TCP 送信者が 2 回の TLP の後に RTO を宣言するのとほぼ同等の動作になります。

この設計は、アプリケーションパターンが PTO 期限切れに影響を与えるため、連続する PTO イベントを使用して持続的輻輳を確立しません。たとえば、それらの間に沈黙期間を持つ少量のデータを送信する送信者は、送信するたびに PTO タイマーを再起動し、確認応答を受信していなくても、PTO タイマーが長期間期限切れになることを防ぐ可能性があります。持続時間の使用により、送信者は PTO 期限切れに依存せずに持続的輻輳を確立できます。

7.6.2. 持続的輻輳の確立 (Establishing Persistent Congestion)

確認応答の受信後、2 つの確認応答要請 (ack-eliciting) パケットが失われたと宣言され、次の条件を満たす場合、送信者は持続的輻輳を確立します:

  • すべてのパケット番号空間で、これら 2 つのパケットの送信時刻間に送信されたパケットは確認されていません。

  • これら 2 つのパケットの送信時刻間の持続時間は、持続的輻輳持続時間(セクション 7.6.1)を超えています。そして

  • これら 2 つのパケットが送信されたときに、事前の RTT サンプルが存在していました。

これらの 2 つのパケットは確認応答要請でなければなりません (MUST)。受信者は、最大確認応答遅延内で確認応答要請パケットのみを確認する必要があるためです。[QUIC-TRANSPORT] のセクション 13.2 を参照してください。

持続的輻輳期間は、少なくとも 1 つの RTT サンプルがあるまで開始すべきではありません (SHOULD NOT)。最初の RTT サンプルの前に、送信者は初期 RTT(セクション 6.2.2)に基づいて PTO タイマーを設定します。これは実際の RTT よりも大幅に大きい可能性があります。事前の RTT サンプルを要求することで、送信者が潜在的に少なすぎるプローブで持続的輻輳を確立することを防ぎます。

ネットワーク輻輳はパケット番号空間の影響を受けないため、持続的輻輳はパケット番号空間を越えて送信されたパケットを考慮すべきです (SHOULD)。すべてのパケット番号空間の状態を持たない送信者、またはパケット番号空間を越えて送信時刻を比較できない実装は、確認されたばかりのパケット番号空間の状態のみを使用できます (MAY)。これにより、誤って持続的輻輳を宣言する可能性がありますが、持続的輻輳の検出に失敗することはありません。

持続的輻輳が宣言されると、送信者の輻輳ウィンドウは最小輻輳ウィンドウ (kMinimumWindow) に減少しなければなりません (MUST)。これは、RTO での TCP 送信者の応答と同様です [RFC5681]。

7.6.3. 例 (Example)

次の例は、送信者が持続的輻輳を確立する方法を示しています。次のように仮定します:

smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay = 2
kPersistentCongestionThreshold = 3

次のイベントシーケンスを考えます:

時刻アクション
t=0パケット #1 を送信(アプリケーションデータ)
t=1パケット #2 を送信(アプリケーションデータ)
t=1.2#1 の確認応答を受信
t=2パケット #3 を送信(アプリケーションデータ)
t=3パケット #4 を送信(アプリケーションデータ)
t=4パケット #5 を送信(アプリケーションデータ)
t=5パケット #6 を送信(アプリケーションデータ)
t=6パケット #7 を送信(アプリケーションデータ)
t=8パケット #8 を送信(PTO 1)
t=12パケット #9 を送信(PTO 2)
t=12.2#9 の確認応答を受信

t = 12.2 でパケット 9 の確認応答を受信したとき、パケット 2 から 8 が失われたと宣言されます。

輻輳期間は、最も古い失われたパケットと最も新しい失われたパケットの間の時間として計算されます:8 - 1 = 7。持続的輻輳持続時間は 2 * 3 = 6 です。閾値に達し、最も古い失われたパケットと最も新しい失われたパケットの間のパケットが確認されなかったため、ネットワークは持続的輻輳を経験したと見なされます。

この例では PTO 期限切れを示していますが、持続的輻輳を確立するために必須ではありません。


7.7. ペーシング (Pacing)

送信者は、輻輳制御器からの入力に基づいて、すべての送信中パケットの送信をペーシングすべきです (SHOULD)。

それらの間に遅延なく複数のパケットをネットワークに送信すると、短期的な輻輳と損失を引き起こす可能性のあるパケットバーストが作成されます。送信者はペーシングを使用するか、そのようなバーストを制限しなければなりません (MUST)。送信者はバーストを初期輻輳ウィンドウに制限すべきです (SHOULD)。セクション 7.2 を参照してください。受信者へのネットワークパスがより大きなバーストを吸収できることを知っている送信者は、より高い制限を使用できます (MAY)。

実装は、輻輳制御器がペーサーとうまく機能するようにアーキテクチャに注意を払う必要があります。たとえば、ペーサーは輻輳制御器をラップして輻輳ウィンドウの可用性を制御するか、ペーサーは輻輳制御器によって渡されたパケットをペーシングアウトできます。

ACK フレームのタイムリーな配信は、効率的な損失回復にとって重要です。ピアへの配信を遅らせることを避けるため、ACK フレームのみを含むパケットはペーシングされるべきではありません (SHOULD NOT)。

エンドポイントは、選択したようにペーシングを実装できます。完璧にペーシングされた送信者は、時間的に完全に均等にパケットを分散します。このドキュメントのようなウィンドウベースの輻輳制御器の場合、そのレートは RTT 上で輻輳ウィンドウを平均化することによって計算できます。時間あたりのバイト数の単位でレートとして表現され、congestion_window はバイト単位です:

rate = N * congestion_window / smoothed_rtt

または時間単位のパケット間間隔として表現されます:

interval = (smoothed_rtt * packet_size / congestion_window) / N

N に小さいが少なくとも 1 の値(たとえば 1.25)を使用することで、RTT の変動が輻輳ウィンドウの利用不足を引き起こさないことが保証されます。

パケット化、スケジューリング遅延、計算効率などの実際的な考慮事項により、送信者は RTT よりもはるかに短い期間でこのレートから逸脱する可能性があります。

ペーシングの 1 つの可能な実装戦略は、リーキーバケットアルゴリズムを使用します。ここで、「バケット」の容量は最大バーストサイズに制限され、「バケット」が満たされるレートは上記の関数によって決定されます。


7.8. 輻輳ウィンドウの利用不足 (Underutilizing the Congestion Window)

送信中バイトが輻輳ウィンドウより小さく、送信がペーシング制限を受けていない場合、輻輳ウィンドウは利用不足です。これは、アプリケーションデータ不足またはフロー制御制限により発生する可能性があります。これが発生した場合、スロースタートまたは輻輳回避のいずれにおいても、輻輳ウィンドウを増加させるべきではありません (SHOULD NOT)。

パケットをペーシングする送信者(セクション 7.7 参照)は、パケットの送信を遅らせ、この遅延のために輻輳ウィンドウを完全に利用しない可能性があります。送信者は、ペーシング遅延なしで輻輳ウィンドウを完全に利用していた場合、アプリケーション制限とみなすべきではありません (SHOULD NOT)。

送信者は、利用不足期間後に輻輳ウィンドウを更新するための代替メカニズムを実装できます (MAY)。これは TCP のために提案されたものなど [RFC7661] です。