なぜWeb APIが突然遅くなるのか?―TCP輻輳制御が支配する「目に見えない渋滞」の正体
現場でインフラを触っていると、「特定の時間帯だけAPIのレスポンスが極端に悪くなる」といった相談をよく受ける。アプリケーションコードには問題がない。ログを見てもバックエンドの負荷は低い。そんな時、多くのエンジニアが盲点とするのが、TCP層における「輻輳制御(Congestion Control)」だ。
今日は、パケットがネットワークという名の高速道路を走る際、いかにして「渋滞」を察知し、自らを律しているのか。その深淵なる世界を紐解こう。
—
1. 輻輳ウィンドウ(CWND)という名の「アクセル」
TCP通信において、送信側が相手からの確認応答(ACK)を待たずに送り出せるデータ量を決めるのが CWND(Congestion Window)だ。これはまさに、ネットワークという道路状況に合わせて踏み込む「アクセルの深さ」に相当する。
この CWND が小さすぎれば帯域を使い切れないし、大きすぎればネットワークがパンクしてパケットロスが発生する。このバランスを動的に調整するのが Slow Start と Congestion Avoidance という二大アルゴリズムだ。
スロースタート(Slow Start):控えめな発進
コネクション開始時、送信側は「この道はどれくらい空いているのか?」を知らない。そこで、まずは CWND を小さく(例えば MSS の10倍程度)設定し、ACKが返ってくるたびに CWND を倍々ゲームで増やしていく。これがスロースタートだ。
輻輳回避(Congestion Avoidance):慎重な巡航
ある一定の閾値(ssthresh:Slow Start Threshold)に達すると、急激な増大はリスクと判断し、CWND を「1 RTT(Round Trip Time)ごとに1 MSSずつ」という緩やかな増加に切り替える。これが輻輳回避フェーズである。
—
2. パケットの挙動を追跡する
現場でトラブルシューティングする際、我々は tcpdump や wireshark を使うが、まずは手元の curl でその片鱗を見てみよう。
# -v オプションでTCPハンドシェイクから通信終了までを詳細表示
# --trace-time でタイムスタンプを付与してRTTの感覚を掴む
curl -v https://api.example.com --trace-time
このとき、もし HTTP/2 や HTTP/3 を使っているなら、CWND の影響はさらに顕著だ。特に QUIC(HTTP/3)はユーザー空間で輻輳制御を行うため、OSのカーネルパラメータに依存せず、より攻撃的なスロースタートが可能になっている。
—
3. Pythonで体感する輻輳制御のロジック
理屈だけでは実感が湧かないだろう。簡易的に CWND の増減ロジックをPythonでシミュレートしてみる。
# 輻輳制御の概念シミュレーション
cwnd = 1.0 # 初期CWND
ssthresh = 64.0 # 閾値
packet_loss = False
def simulate_tcp_flow(round_trips):
global cwnd, ssthresh
for i in range(round_trips):
if cwnd < ssthresh:
# スロースタート: 指数関数的に増加
cwnd *= 2
print(f"RTT {i}: スロースタート中, CWND={cwnd}")
else:
# 輻輳回避: 線形的に増加
cwnd += 1
print(f"RTT {i}: 輻輳回避中, CWND={cwnd}")
# パケットロスが発生したと仮定
if i == 5:
print("--- パケットロス検知! ---")
ssthresh = cwnd / 2
cwnd = 1.0 # 再びスロースタートへ
break
simulate_tcp_flow(10)
このコードが示す通り、ひとたびロスが発生すれば、苦労して積み上げた CWND は一気にリセットされる。これが「APIの応答が急に詰まる」原因の正体だ。
—
4. 現場で役立つチューニングの心得
もし君が運用しているサーバーでパケットロスが頻発しているなら、まずはカーネルパラメータを確認してほしい。
# Linuxカーネルの輻輳制御アルゴリズムを確認
sysctl net.ipv4.tcp_congestion_control
# 現代の推奨は 'bbr' (Google開発)
# 損失ベースではなく帯域遅延積をベースに制御するため、ロスに強い
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
bbr(Bottleneck Bandwidth and Round-trip propagation time)は、従来の Cubic などとは異なり、パケットロスを「混雑」と誤認せず、純粋なスループットを最大化するように設計されている。グローバルに展開するAPIサーバーであれば、この切り替えだけでレスポンス速度が改善するケースは極めて多い。
—
最後に:ネットワークを「意識」するエンジニアへ
「APIが遅い」と言われたとき、多くの開発者はデータベースのクエリやアプリの計算処理ばかりを疑う。しかし、凄腕のエンジニアは、パケットが通り抜ける物理的・論理的な経路、そしてTCPが自律的に制御するその「呼吸」にまで意識を向ける。
ネットワークは生き物だ。その挙動を深く理解し、適切なパラメータでチューニングを施すこと。それが、ユーザーに「速い」と感じさせるための、我々インフラ屋の矜持である。
さあ、次は tcpdump を片手に、パケットの往来を眺めてみるとしよう。きっと、昨日までとは違う景色が見えるはずだ。
コメント