QUICでDatagramなしに適時性を実現する
(quic.video)- ライブ動画やインターネット上の会話で重要なのは「信頼性のない配送」そのものではなく、古いデータを押しのけて最新データを時間どおりに届けること
- ルーターが混雑したパケットを捨てずにキューへ長くため込むとbufferbloatが発生し、リアルタイムサービスではバッファリングより悪い遅延が起こりうる
- UDPの上に直接プロトコルを作ると、再送、輻輳制御、暗号化、RTT推定、パス検証、フロー制御などを再実装する必要があるため、QUICライブラリを使うほうが安全
- QUICだけでも、遅延ベースの輻輳制御、独立したストリーム分割、優先順位付けを組み合わせることで、データグラムなしに適時性を実現できる
- QUIC・WebTransport・MoQ標準にはデータグラム対応が入っているが、新しい動画プロトコルをUDPの上に再び積み上げるより、Media over QUICの流れに合わせるほうがよいという結論
目標は「不安定さ」ではなく適時性
- TCPとUDPの選択は、よく「信頼性のある配送」と「信頼性のない配送」として説明されるが、アプリケーションが求めているのは不安定さそのものではない
- ライブ動画やインターネット上の会話では、古いデータをすべて受け取ることより、最新データが先に到着することのほうが重要
- リアルタイム会話で、顔の上にバッファリングのスピナーが表示されたり、5秒前の音声を聞いたりする状況は避けるべき
- ライブ動画業界とゲーム業界は、このような適時性のためにTCPストリームではなくUDPデータグラムをよく使う
データグラムとネットワークキュー
- データグラムは送信元アドレスから宛先アドレスへ送られる0と1の封筒であり、一般に1200バイトが安全なサイズとして扱われる
- データグラムはひそかに失われることがあり、順序が入れ替わって到着することもある
- 物理層ではデータがアナログ信号に変わって媒体を通り、シリアライズ、デシリアライズ、バッファリング、キューイング、再送、破棄、破損、遅延、並べ替え、重複、損失が起こりうる
- ネットワークに多すぎるデータが入ってくると、ルーターは任意のビットではなくパケット境界でデータを捨てる
Bufferbloatと輻輳制御
- ルーターがパケットをすぐに捨てずキューにためると、bufferbloatが発生する
- bufferbloatはすべてのパケットを数秒単位で遅延させることがあり、リアルタイム配送にとって最悪の状況を作る
- キューイングを避けるには、パケット到着時刻のフィードバックでルーターのキューイングを推定し、送信者が送信量を減らしてキューを空にする必要がある
- この領域は輻輳制御に該当し、無制限の速度でパケットを送る方式は惨事につながりうる
UDPの上に直接作ると背負う機能
- UDPを直接使ってトランスポートプロトコルを作るには、少なくとも次の機能が必要
- よりよいプロトコルを作るには、さらに次の機能まで必要
- 完成度を高めるには、運用環境とデプロイまで考慮する必要がある
- WebRTC、SRT、Sye、RISTのようなライブ動画プロトコルは、UDPの上に作られた事例
- 新しいプロトコルを自作するより、QUICライブラリを使うほうがよいという判断につながる
QUICストリームで適時性を作る
- QUICで適時性を達成する方法は3つに整理できる
- バッファ膨張の回避: BBRのような遅延ベースの輻輳制御がキューイングを検知し、送信量を減らす
- WebRTCのtransport-wide-ccは、よりよい方式の例と見なせる
- ストリーム分割: 各ストリーム内部のバイトは順序があり信頼性をもって配送され、ストリームは動画フレーム、ゲーム更新、チャットメッセージ、JSON blobのような原子的な単位になりうる
- ストリームの優先順位付け: ストリームは独立しているため順序と関係なく到着でき、QUICスタックに重要なストリームを先に届けるよう指示できる
- 優先順位の低いストリームは飢餓状態になることがあり、帯域幅の無駄を避けるには閉じることができる
- このアプローチがMedia over QUICの核心
- データグラムのfire-and-forgetな性質は、リアルタイム遅延が必要なときにだけ適しており、それ以外ではQUICストリームを使える
データグラムをめぐる妥協と例外
- QUICと関連標準にはデータグラム対応も含まれる
- QUICは拡張によりデータグラム対応を提供する
- WebTransportはデータグラム対応を要求する
- 最新のMoQバージョンはデータグラム対応を追加する
- 次のMoQバージョンはデータグラム対応を要求する予定
- データグラム対応は、実装が些細で実験を許容できるという理由で含まれることがある
- OPUSはFEC対応を内蔵しており、MoQが各オーディオ「frame」をデータグラムとして送る機能をサポートする例になる
- 古いプロトコルであるDNSは例外にできるが、新しい設計ではDNS over HTTPSのような方向に従うほうがよいと見ている
- 新しい動画プロトコルをUDPの上に再び作るより、Media over QUICに参加せよという結論につながる
1件のコメント
Hacker News のコメント
たとえば NB-IoT のように最悪の場合の往復遅延が 10 秒になる環境では、ハンドシェイクと MTU 探索に往復時間が浪費され、もはや役に立たないデータも送信し続けようとし、カバレッジが悪化して遅延が増えると、TCP はそれを輻輳によるパケット損失と見なして帯域幅を絞る
また、ロードバランサーやミドルボックスが「4 秒間応答がないなら消えたのだろう」と接続を切ることもあり、TCP がデータ構造を考慮せずにパケットを分割するため、全体を受け取るまで解釈できない点も惜しい
場合によっては有用だが、n 番目がまだなくても n+1 番目を活用できるケースは多い
大きなファイル転送では消失訂正符号で 5% のパケット損失を自動処理したり、fountain code を使って受信者が「全部受け取った」と言うまで送り続けたりすることもできる
fountain code は深宇宙探査機がデータを送る方式で、木星や火星までの遅延はかなり深刻だ
TCP ハンドシェイクが終わらないと TLS ハンドシェイクを始められないが、QUIC には初期ハンドシェイク内で TLS ネゴシエーションを処理するプロトコルレベルのサポートがある
ネットワークプロトコルと暗号化をゆるく組み合わせられるほうがよりエレガントに見えるが、今やほぼすべての転送が暗号化される世界では、接続ごとに往復時間を 1 回減らせるという実用上の利点のほうが大きく見える
R&D 側で QUIC を使う新しいシステムを作り、順序が入れ替わって到着する問題の大半を解決したが、アダプターなしで直接サポートしなければならないサードパーティ製センサーが UDP しか扱えないため、今でもすべてに UDP データグラムを使っている
より一般的でより良い表現は best-effort であり、UDP はデータグラム配送をベストエフォートで試みるが、データグラムが破棄される可能性があるだけだ
だからといって UDP が本質的に信頼できないという意味ではない
https://en.wikipedia.org/wiki/Best-effort_delivery
実際には A から B へメッセージを送るために最後まで頑張るという意味ではなく、「努力はした」に近い
経路上のルーターが輻輳していたり、リンクフラップによって高速な再ルーティングの前に 50ms ほどブラックホールが生じたりしても、「まあ、やってはみた」ということになる
一方で TCP の信頼的配送は何度も再試行し、アプリケーションに順序のそろったデータストリームを提供する
reliable/unreliable も悪い用語かもしれないが、best-effort のほうが優れているとは言いにくい
信頼的でないシステムは 95% くらいは素晴らしく、生のスループットにも良いが、最後の 5% が非常に大きな差を生むことが多い
「努力」は通常、困難を前にしてある程度の粘り強さを意味するが、リソース問題が起きたからといってパケットを捨てるのは努力とは呼びにくく、best effort ならなおさらだ
法律・ビジネス用語の “best efforts” は確約よりは弱いが、あからさまに手を引くことではない。しかしネットワーキングでの使い方はそれとはかなり違う
それとは別に、UDP と TCP のチェックサムも、データグラムが配送されたときの完全性を十分に保証できず、ハードウェアより少し良い程度だ
ただし best-effort は配送保証のために何か努力をしているような印象を与えるが、実際にはおかしそうに見えるパケットや、運悪く満杯のバッファに出会ったパケットをそのまま捨てるだけだ
lossy が気に入っているが、これは「難しい問題は 2 つだけ」に当たる命名問題だ
パケットが届かなければならないなら TCP を使い、大して問題でなければ UDP を使えばよい
単純化した説明ではあるが、best effort は間抜けな用語で、そこに努力はない
輻輳制御は確かに必要だが、それ以外には疑問が多い
データグラム優先の世界なら、複数のデータリンクを非常に効率よく束ねたり、ネットワーク境界をまたいで移動しても接続を切らずにローミングしたりすることに問題はなかったはずだ
多くのアプリケーションは順序が入れ替わったフレームも追加コストなしで処理でき、UDP モデルに合わせて書けばはるかに速くなり得る
実際、信頼性の低い転送方式に移行したからといって、ソフトウェアが自動的により信頼的または効率的になるわけではない
むしろチームが対処すべき失敗モードと複雑さが大きく増える
ただし接続のどこかに細いボトルネックがあるなら、どうせそのボトルネックで捨てられるパケットを大量に作り出しても、あまり意味はなさそう
Webサイト、音声、動画はたいてい順序が入れ替わったフレームとは相性が悪く、多くの場合、音声や動画が途切れることも望まれていない
一部のビデオゲームは欠落したパケットを無視してもよいが、そういうものはすでにUDPで書かれている
そうしたパケットは再送されないはずなので、過剰なトラフィックを減らす効果的な方法だという考えからだ
今ではUDP上で積極的に再送するプロトコルが出てきているが、それが状況をどう変えたのか気になる
数年前、QUICがこのためにHTTP/1・HTTP/2と比べて再送の問題を抱えていた記憶もある
もっと代表的な文言を記事中で見つけられるなら、また変更できる
HNのタイトル指針である「元のタイトルを使う。ただし誤解を招く場合や釣り気味の場合は例外」に従ったもの: https://news.ycombinator.com/newsguidelines.html
アプリケーションによっては妥当である
例えばリアルタイムのマルチプレイヤーゲームで処理が遅れると、ゲーム状態はすでに変わっているため、遅れた項目はもはや重要ではない
高速取引アプリケーションでも、状況によっては100ms前の出来事ではなく、最新の市場データだけが重要になる
記事はUDPが適している例としてゲームやライブ動画も挙げている
最初に「通念」を提示し、その後それに反論する構成だ
例えばDHCP、SLAAC、UPnP、mDNS、tinc、BitTorrentのようなローカル探索、ローカルネットワークストリーミングのようなブロードキャスト、WireGuard、IPSec、OpenVPN、VLANのようなカプセル化が該当する
再送はもちろん、順序並べ替えのためのバッファリングだけでも遅延が増えるので、誤り訂正やパケットロス隠蔽で損失を受け入れるほうがよい
UDPとTCPは動作とトレードオフが異なるので、ユースケースに合わせて選ぶ前に理解すべきだ、というだけの話だ
「Xは絶対にするな」式の門番気取りは不要だ
タイトルだけ見ても、UDPを使わせないように門番気取りをする記事ではないことはかなり明らかだ
実際、記事の最後で筆者はUDPベースのQUICの使用を提案している
もちろん、はるかに多くの細部を自分で面倒見る必要がある
おまけに、ネットワーキングの低レベルな側面を学ぶ良い方法でもある