SACK と RACK:選択確認応答と時刻ベース再送

複数欠落でも穴だけを正確に埋め、損失検知の遅れと偽再送を同時に減らせる。選択確認応答 SACK と時刻ベースの RACK-TLP の内部動作を、累積 ACK の限界から解き明かします。

応用TCPSACKRACK再送制御損失回復
3つの要点
  1. SACK は累積 ACK の先にある飛び地の受領範囲を伝え、scoreboard により複数欠落を同時に特定して並行再送できる。
  2. RACK は重複 ACK の個数ではなく各パケットの送信時刻と最新 RTT で損失を判定し、reordering に強く末尾損失も検知する。
  3. TLP はテール損失を探りプローブで埋め、DSACK は偽再送を事後検知して縮めすぎた cwnd を取り戻す。

累積 ACK が抱える二つの限界

TCP の確認応答は累積 ACK です。受信側は「次に期待するシーケンス番号」を 1 点だけ返し、そこまでが連続して届いたことを示します。素朴で堅牢ですが、損失回復の観点では二つの限界を持ちます。

ひとつは情報量の不足です。1 つの RTT で複数のセグメントが落ちると、累積 ACK が指せるのは先頭の穴 1 個だけ。送信側は 2 個目以降の穴の位置を知れず、回復が穴の数だけ直列化します。もうひとつは損失判定の仕組みです。古典的な再送は重複 ACK が 3 つ届いたことを損失の合図にしますが(再送制御の内部(RTO・ファストリトランスミット・SACK) 参照)、この「個数ベース」の判定は経路の並べ替え(reordering)に弱く、ウィンドウ末尾の損失では重複 ACK 自体がそもそも返りません。

SACK は前者を、RACK-TLP は後者を解く機構です。順に見ていきます。

SACK:飛び地の受領範囲を伝える

SACK(Selective Acknowledgment、RFC 2018)は、累積点より先で飛び飛びに届いた範囲を TCP オプションで明示通知します。各ブロックは [左端, 右端) 形式で、TCP オプション領域 40 バイトの制約から最大 3 個(タイムスタンプオプション併用時)まで載ります。送信側はこれを scoreboard(各セグメントの送達状態を持つ管理表)に反映し、SACKed と印の付いた範囲を再送対象から外します。

受信状況:  [1000-2000] 欠落 [3000-5000] 欠落 [6000-7000]
            └ 累積ACK     └ SACKブロック1   └ SACKブロック2

ACKオプション:
  ack = 2000                       ← 累積:2000まで連続受領
  SACK: {3000-5000}, {6000-7000}   ← 累積より先の受領済み範囲

この例で再送すべきは 2000-30005000-6000 の 2 区間だけ。SACK があれば 1 RTT で両方の穴を特定し並行再送でき、累積 ACK のみのときのような直列化が消えます。SACK は SYN で SACK-Permitted を双方が交換したときだけ有効です。

SACKベース回復のループ防止:pipe推定

SACK ベースの損失回復(RFC 6675)は、scoreboard から「今ネットワーク上に滞留している未確認データ量」pipe を推定し、cwnd − pipe ぶんだけ送出を許可します。SACKed と再送済み(retransmitted)を区別して数えることで、同じ穴を二重に再送したり送りすぎたりせず、ファストリカバリ中も輻輳ウィンドウの意味を保ったまま穴埋めと新規送信を進められます。

RACK:時刻ベースで損失を判定する

SACK は「どこが届いたか」を正確にしますが、「いつ損失とみなすか」は依然として重複 ACK の個数に依存していました。RACK(Recent ACKnowledgment、RFC 8985)はこの判定を時刻ベースに置き換えます。アイデアは単純です。あるパケットより後に送ったパケットの ACK(SACK 含む)が既に届いているなら、先に送ったそのパケットは十分な時間が経っても届いていない=失われた可能性が高い、と推論します。

RACK は各パケットの送信時刻を記録し、最新の RTT に小さな余裕 reorder_window を加えた待機時間を設けます。あるパケットについて「より新しいパケットが ACK 済み」かつ「送信からの経過時間が RTT + reorder_window を超えた」場合に損失と判定します。

判定ロジック(概念):
  最も新しくACK/SACKされたパケットの送信時刻を rack.xmit_ts とする
  各未確認パケット p について:
    if p.xmit_ts < rack.xmit_ts            # pより後のものが先に届いた
       and now - p.xmit_ts > RTT + reorder_window:
       p を損失とマークして再送

ここで個数(重複 ACK の数)はどこにも出てきません。「3 つ」という固定しきい値を捨て、経過時間で測ることが本質です。これにより二つの長年の問題が解けます。

観点重複ACK 3個ベース(従来)RACK(時刻ベース)
損失の合図同一ackが3回届くより新しいパケットがACK済み+RTT経過
reordering耐性弱い(並べ替えで誤検知/誤再送)強い(reorder_windowを動的調整)
ウィンドウ末尾の損失後続が無く重複ACKが出ずRTO頼みTLPと連携し時刻で検知可能
1パケットだけ送って損失重複ACKが揃わず検知不能RTT経過で損失と判定できる
判定単位ACKの個数各パケットの送信時刻

並べ替えへの強さが効くのは、RACK が reorder_window を経路の実測並べ替え量に応じて伸縮させるからです。DSACK で「無駄な再送だった」と分かるたびに窓を広げ、安定すれば狭めます。固定しきい値の硬直性がなく、並べ替えの多い経路では保守的に、少ない経路では機敏に振る舞います。

TLP:テール損失をプローブで埋める

RACK は「後続パケットが既に ACK 済み」を手掛かりにします。ではウィンドウの最後のパケットが落ちたらどうなるでしょう。後続が存在しないため新しい ACK が返らず、RACK も重複 ACK も発火しません。残るは秒オーダーの RTO だけ——これがテール損失(tail loss)で、短いフロー(HTTP リクエスト 1 個など)で体感遅延を最も悪化させる要因です。

TLP(Tail Loss Probe)はこれを埋めます。データを送り終えてしばらく ACK が来ないとき、RTO よりずっと短い PTO(Probe Timeout、おおむね 2 × RTT 程度)でプローブを 1 つ送ります。プローブは末尾の未確認セグメントの再送、または送るべき新しいデータです。プローブへの ACK や SACK が返れば、そこで RACK が損失を判定して正規の回復に入れます。秒オーダーの RTO を待たず、数 RTT で末尾損失から立ち直れるわけです。

RACKとTLPは役割分担している

RACK は「届いた ACK の時刻情報から損失を推論する」検知器、TLP は「ACK が止まった末尾に意図的にトラフィックを足して ACK を引き出す」誘発器です。RFC 8985 が両者を一体で規定するのはこのためで、RACK が観測に基づく受動的判定、TLP が能動的な探りという補完関係にあります。

DSACK:偽再送を事後検知して取り戻す

時刻ベースや早すぎる RTO は、ときに届いていたデータを再送します(spurious retransmission)。受信側は同じデータを二度受け取ったことを DSACK(Duplicate-SACK、RFC 2883)で通知します。最初の SACK ブロックに累積 ACK 以下、または既受領済みの範囲を載せることで「重複受信」を表します。

送信側はこれを受けて二つの補正をします。ひとつは RACK の reorder_window を広げること。偽再送は並べ替えを損失と誤判定した証拠なので、次回はより慎重に待ちます。もうひとつは輻輳ウィンドウの巻き戻しです。偽再送に反応して cwnd を半減していた場合、損失は実在しなかったのだから縮めた分を取り戻します。これにより、reordering の多い経路でスループットを不当に失わずに済みます。

試験・面接で問われる急所

「SACK は飛び地の受領範囲を伝え複数欠落を並行回復、RACK は重複ACKの個数ではなく送信時刻と最新RTTで損失を判定」。「RACK の核心は個数しきい値を捨てて経過時間で測ること、ゆえに reordering 耐性が高く末尾損失も拾える」。「TLP は短い PTO でプローブを送りテール損失を RTO 前に埋める」。「DSACK は偽再送を事後検知し reorder_window 拡大と cwnd 復元に使う」——この4点が頻出です。

全体像:検知の精度を時間軸で上げる

SACK から RACK-TLP への流れは、損失回復を空間(どこが穴か)と時間(いつ損失とみなすか)の二軸で精密化した歴史です。SACK が穴の位置を点から範囲へと正確にし、RACK が判定基準を個数から時刻へと移し、TLP が末尾の死角を能動的に埋め、DSACK が誤りを事後に正します。

横にスクロール

全体像:検知の精度を時間軸で上げるについて、通信経路、責任境界、内部状態、障害と性能の観測点を示した解説図
全体像:検知の精度を時間軸で上げるを題材に、通信経路と責任境界を追い、障害調査と性能判断に必要な観測点を整理します。

現代の Linux はこの一式を既定で使い、QUIC も同じ思想を採用します。QUIC はパケット番号が再送で必ず増える設計のため、ACK の曖昧性が原理的に無く、RACK 相当の時刻ベース判定をより素直に実装できます(QUIC の内部構造(ストリーム多重化と0-RTT) 参照)。損失検知の精度は輻輳制御の挙動を直接左右するため、CUBIC のようなウィンドウ制御(CUBIC の三次関数ウィンドウ制御)や輻輳制御の系譜(TCP輻輳制御の系譜(Tahoe→Reno→CUBIC→BBR))と合わせて読むと、SACK/RACK が性能のどこに効くかが立体的に見えてきます。

ネットワークの記事ガイド

SACK と RACK:選択確認応答と時刻ベース再送を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

TCP

比較で見る軸

難易度: advanced / カテゴリ: ネットワーク / タグ数: 5

導入後に効く点

RACK は重複 ACK の個数ではなく各パケットの送信時刻と最新 RTT で損失を判定し、reordering に強く末尾損失も検知する。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
ネットワーク
タグ数
5

判断チェックリスト

  • 自社の用途が「TCP / SACK」に近いか確認する。
  • 強みである「SACK は累積 ACK の先にある飛び地の受領範囲を伝え、scoreboard により複数欠落を同時に特定して並行再送できる。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

TCPSACKRACK再送制御損失回復
参考: 公式情報