ゲームラグ白書 › TCP再送の根本原因
thin streamの遅い回復 Thin streams fall back to RTO
原因ID rt-thin · 主担当 ゲーム開発チーム・サーバー開発 · 副担当 インフラチーム・サーバーインフラ, ゲーム開発チーム・クライアント開発
図と実験のあるメインページでこのカードを開く →
ゲームのように小さなパケットをまばらに送ると、「後続のパケット3つ」がそろう前にRTOが先に来ます。大容量の転送と比べて、同じパケットロスでもはるかに長く止まります。
なぜ パケットの間隔が100ms前後なので、ACK待ちのパケット(in-flight)が数個しかない → すると 重複ACKが3つそろうには300ms以上かかるため、RTO(Ping + 200ms)が先に発動、連続したパケットロスなら2倍ずつ → 画面では パケットロス1回で0.3秒前後止まり、送り直したものまで失うと1秒近く止まった後に早送り
- 症状
- フリーズ, 早送り
- 要因
- パケットロス, ストール
- 誰に起きるか
- 自分だけ, サーバー全体
- いつ
- ときどきランダムに
- 担当
- 主担当 ゲーム開発チーム・サーバー開発 · 副担当 インフラチーム・サーバーインフラ, ゲーム開発チーム・クライアント開発
- ゲーム開発チームの対応
- サーバー:TCP_NODELAYを有効にする(Nagleアルゴリズムが有効だと、RACKが判定に使う後続パケットがない)、リアルタイムのパケットはUDP上の独自の再送で。クライアント:TCP_NODELAYを有効にする、リアルタイムのパケットはサーバーと同じ方式(UDP)で。
- インフラチームの対応
- RACK-TLPを使う(最新Linuxのデフォルト)、tcp_thin_linear_timeoutsで連続したRTOが2倍に延びないようにする。
- 数値の目安
- パケット間隔が100ms、Pingが60msなら、高速再送までは約360ms(後続のパケット3つが届き、その確認応答が戻ってくるまで)、RTOは約260msです。RACKを使えば、次のパケットの確認応答が戻ってくる約160msの時点ですぐに送り直します。パケット間隔が200msを超えると、RACKでもRTOより速くはなりません。
- グラフでは
- 途切れた後にまとめて到着 · 接続ごとの受信量、RTOの満了数
- 確認箇所
- nstatのTcpExtTCPTimeouts(RTOの満了)・TcpExtTCPFastRetrans(高速再送)・TcpExtTCPLossProbes・TcpExtTCPLossProbeRecovery(TLP)の増分を比較し、ゲームの接続をss -tiで見てrto・backoffを確認。サーバーのnet.ipv4.tcp_recovery・tcp_early_retrans・tcp_sackの値も確認
- 該当する場合
- 再送のうちRTOの満了が高速再送より多く、ゲームの接続でbackoffが0より大きい状態(RTOの最中)がよく見られる。止まっている間の受信量が0で、回復すると一気にまとめて届く
- 該当しない場合
- 同じサーバーの大容量の転送も同じように長く止まるなら、通信パターンとは関係ないパケットロスの問題。SACK・タイムスタンプのない接続に偏るなら「中間機器によるTCPオプションの除去」
- 確認手段
- インフラのツールで確認(ゲームコード不要)
- もっと詳しく
- Linuxには以前、thin stream向けに重複ACK1つで再送するオプション(tcp_thin_dupack)もありましたが、2017年に廃止され、今はRACKがその役割を担っています。Nagleアルゴリズムが有効だと(TCP_NODELAYが無効)、失ったパケットの確認応答を待つ間は新しいパケットも送らないため、RACKが判定に使う後続パケットがなく、RTOまで待つことになります。
出典
- Thin-streams and TCP Linux kernel
ゲームのようにまばらに送るthin streamでは高速再送がうまく働かず、長いタイムアウトに頼ることになる、ACK待ちのパケット(in-flight)が4個未満が基準 - RFC 5681: TCP Congestion Control IETF
3つ目の重複ACKで高速再送 - RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP IETF
RACKは後から送ったパケットが届いたことをもとにロスを判定、TLPの待ち時間は2・SRTT(確認応答のないパケットが1つだけなら遅延ACKの分の余裕を加える) - include/net/tcp.h Linux kernel
TCP_RTO_MINは200ms、thin streamの判定(in-flightのパケットが4個未満)と線形の再試行6回 - tcp: remove thin_dupack feature Linux kernel
2017年1月にthin_dupackを削除(Linux 4.11)、RACKがその役割を担うという説明 - IP Sysctl Linux kernel
tcp_thin_linear_timeouts:thin streamなら最大6回までRTOを2倍に延ばさない(デフォルトは無効) - tcp(7) — Linux manual page Linux man-pages
TCP_NODELAYはNagleアルゴリズムを無効にする - net/ipv4/proc.c Linux kernel
nstatに表示されるカウンター名TCPTimeouts・TCPFastRetrans・TCPLossProbes・TCPLossProbeRecovery - net/ipv4/tcp_timer.c Linux kernel
TCPTimeoutsは再送タイマー(RTO)が満了したときに増加 - SNMP counter Linux kernel
TcpExtTCPFastRetrans(Loss状態でないときの再送)、TcpExtTCPLossProbes(TLPを送信)・TcpExtTCPLossProbeRecovery(TLPでパケットロスを回復) - ss(8) — Linux manual page iproute2
ss -iのrto(ms)、backoff(RTOが2倍に延びた回数)
あわせて読みたい原因
同じ層:TCP再送の根本原因
同じ症状(フリーズ)を起こすほかの層の原因
図と実験のあるメインページでこのカードを見る