サイト:https://jungrok5.github.io/mmo-lag-anatomy/ja/ 原因ページ:https://jungrok5.github.io/mmo-lag-anatomy/ja/c/[原因ID].html # ゲームラグ白書 ナレッジデータ 自動生成(2026-10-03、`node tools/export.cjs`)。手で編集せず、src/js/を修正してから再出力する。 原因228件、用語135件。原因は**ID**で指す。サイトのURLの後ろに`#c-ID`を付けると、その原因カードに移動する。 ## 担当コード | コード | チーム | 担当 | 範囲 | |---|---|---|---| | cli | ゲーム開発チーム | クライアント開発 | ゲームクライアントのコード:フレーム・GC・ロード、補間・外挿・予測、クライアントのネットワーク処理(ハートビート送信・自動再接続を含む) | | srv | ゲーム開発チーム | サーバー開発 | ゲームサーバーのコード:ティック・スレッド・ロック、同期設計、接続処理(acceptループ・listenの引数)、ハートビートへの応答・切れた接続の後始末、ソケットオプション、クエリ・トランザクション設計 | | net | インフラチーム | ネットワークインフラ | 回線とデータセンターのネットワーク機器(スイッチ・ルーター・ファイアウォール・ロードバランサー・DDoS対策)、クラウドのネットワークACL・VPCルーティング・ロードバランサー、通信事業者・ピアリング | | sys | インフラチーム | サーバーインフラ | サーバー機器・クラウドインスタンス(セキュリティグループ・接続追跡を含む)、OS・カーネル設定、NIC、デプロイ・監視環境 | | dba | インフラチーム | DBインフラ | DBサーバー・ストレージ、DBの設定・レプリケーション・バックアップ、キャッシュサーバー | | ext | 外部 | 外部 | ユーザーのPC・家庭内ネットワーク、通信事業者の区間(自社の契約外)、クラウド事業者。直接は直せないため、案内・依頼・回避策で対応 | 各原因の「主担当」は根本原因を取り除く担当、「副担当」は実際に対応すべきことがある担当である。 ## 症状 - **カクつき**(`stutter`、別名:カクカク、引っかかり、スタッター、フレーム落ちのような感じ):動きが滑らかでなく、短く止まっては動くのを繰り返します。 Ping値に問題がなければ自分のPCのフレーム(クライアント・OS)の問題、PingがばらつくならWi-Fi・回線のジッターの可能性が高いです。ただしゲーム内のPing表示はたいていフレームごとに回るゲームループの中で計測しているため、フレームが跳ねるとPingの数値も一緒に跳ねることがあります。 - **ワープ**(`teleport`、別名:瞬間移動、テレポート、止まって飛ぶ):キャラクターが途中の移動なしに、離れた位置へ一瞬で移ります。 たいていは、しばらくパケットが途切れたことを意味します。パケットロス、回線の瞬断、サーバーの停止、外挿の失敗を疑います。ほかの人は問題ないのに一人だけワープするなら、まずその人の回線を疑います。 - **引き戻し**(`rubber`、別名:巻き戻り、ラバーバンド、位置が戻される):自分のキャラクターが前に進んでいたのに、今通ってきた位置へ引き戻されます。 自分の画面(予測)とサーバーの判定が食い違っています。自分の入力がサーバーに届かなかったか(パケットロス)、サーバーの移動検証に弾かれたか、双方の移動計算が一致していません。 - **早送り**(`burst`、別名:一気に動く、早回し、まとめて処理):止まっていた画面が動き出すと同時に、たまっていた動き・ヒット・ダメージが一気に早回しで流れます。 どこかでたまっていたパケットが、一度に解放されました。TCPの再送待ち、サーバーの追いつき処理、クライアントの処理遅れが代表的です。 - **スローモーション**(`slowmo`、別名:世界全体がスローになる、処理落ち、全体的にもっさり):すべてがゆっくり動きます。スキルの発動やモンスターの移動が間延びして見えます。サーバーの設計によっては、速度はそのままでカクつき・ワープとして現れることもあります。 サーバーがティックを時間内に終えられていません。回線は正常なのでゲームの外で測ったPingは変わらず、ゲーム内のPingはサーバーの処理待ちが含まれていると少し上がることがあります。人数の急増、視界計算、ブロードキャスト、メモリ不足を確認します。 - **入力遅延**(`delay`、別名:反応が遅い、もっさり、操作が重い、手応えがない):押してから結果が出るまでに時間がかかります。画面そのものは滑らかな場合もあります。 往復時間(Ping)が長いか、どこかでキューが詰まっています。距離、ルーターのキュー、Nagle(小さなパケットをまとめて送るTCPの機能)、サーバーのキューを確認します。Pingが低いのにいつも操作が重いなら、V-Sync・低いFPSのような自分のPC側か、操作のたびにサーバーの確認を待つ設計(同期方式の章)を疑います。 - **フリーズ**(`freeze`、別名:固まる、止まる、応答なし):画面内のすべてが少しの間(0.5秒〜数秒)止まってから、また動き出します。 サーバーが丸ごと止まったか(GC、デッドロック、同期呼び出し)、回線が一瞬切れたか、自分のPCが止まりました。 - **不発・ロールバック**(`dropped`、別名:スキル不発、入力が食われる、アイテムが元に戻る、取引失敗):確かにやったはずの行動がなかったことになるか、結果がしばらく経ってから覆ります。 リクエストが消えたか(パケットロス、キューのあふれ)、サーバーが自分の画面とは違う判定をしたか(判定タイミングのずれ、先行演出の後の拒否)、保存の途中で失敗しました(DBのロック・障害、サーバーのクラッシュ)。 - **切断**(`disconnect`、別名:回線落ち、落ちる、サーバーとの接続が切断されました):プレイ中に接続が切れ、ログイン画面や再接続の画面に戻されます。 タイムアウト時間内にパケットが一つも届きませんでした。長い回線断、アイドルタイムアウト、サーバーのクラッシュ・再起動、タイムアウトより長く止まったサーバーや自分のPC(長いロード)を確認します。案内なしにゲーム自体が終了したなら、接続よりもクライアントの強制終了(クラッシュ、メモリ不足)を先に疑います。 - **接続不可・無限ロード**(`noconnect`、別名:ログインできない、ロードが終わらない):ゲームに入れない、またはロード・入場画面で止まったままになります。 新しい接続を受け付ける場所(サーバーの接続待ちキュー、ファイアウォール、ログインサーバー、DB)が満杯になっています。メンテ明けに特に多く起きます。 - **表示されない・ゴースト**(`invisible`、別名:NPCが見えない、透明なキャラ、倒したはずのモンスターが立っている):いるはずのNPC・モンスター・プレイヤーが自分の画面にだけいない、またはすでに消えたオブジェクトが自分の画面にだけ残っています。 速度の問題というより、パケットが一つ抜けたか描画に失敗した状態です。チャンネル・フェーズの違い、出現・消滅通知の欠落、ロード中の破棄、アセットのロード失敗を確認します。視界から外れて戻ってきたときに表示されるかどうかが、決定的な手がかりです。 ## 4つの要因 - **遅延**(`lat`、Latency):距離、キュー、処理時間のために、すべてのパケットが一様に遅れて届きます。 ゲーム側の対処:自分の操作は予測・先行演出で先に見せ、判定はサーバーが過去の時点に巻き戻して合わせます(ラグコンペンセーション)。 - **ジッター**(`jit`、Jitter):平均は問題なくても、あるパケットは早く、あるパケットは遅れて届きます。Wi-Fi、混雑した回線、高負荷のCPUが原因になります。 ゲーム側の対処:補間バッファに少しためてから、一定のペースで取り出して描画します。ジッターがバッファより大きいと隠しきれません。 - **パケットロス**(`loss`、Packet loss):あふれたキュー、電波干渉、故障した機器がパケットを破棄します。回線の瞬断も、連続したパケットロスです。 ゲーム側の対処:UDPのゲームは欠けた分を補間・外挿で埋め、自分の入力は重ねて送ることで1〜2個失っても補います。TCPは失ったパケットを受け取り直すまで、後続のパケットをゲームに渡しません。 - **ストール**(`stall`、Stall):サーバーのティックが遅れたり止まったり(GC、ロック、同期呼び出し、過負荷)、自分のPCのフレームが止まったりします。回線に問題がなくても起きます。 ゲーム側の対処:止まっていた分の処理をまとめて実行して追いつくか、飛ばすか、ゆっくり進むままにします。 ## 原因 ### L1 クライアントのゲームプロセス(原因16件) #### cg-hitch · フレームタイムのスパイク · Frame hitch 1フレームの計算に普段の数倍の時間がかかり、画面が一瞬止まります。 - なぜ → すると → 画面では:スキルエフェクトの急増、大量スポーン、UI全体の更新が1フレームに集中 → 16.7ms以内に終わらず、50〜300msかかる → 画面が一瞬止まり、次のフレームで全員が一気に動く - 症状:カクつき, フリーズ / 要因:ストール - 誰に:自分だけ / いつ:人が集中したとき, 特定の操作をしたとき, ときどきランダムに - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:重い処理を複数フレームに分散、プロファイラーで跳ねるフレームを特定、エフェクト数の上限設定。 - 数値の目安:60FPSの1フレームは16.7ms。50msを超えるフレームが1つあるだけでも「カクついた」と感じやすくなります。 - グラフでは:不定期なスパイク(フレームタイム) - 確認箇所:PresentMonで、プレイ中のフレームタイム(FrameTime)と、そのフレームにCPU・GPUが費やした時間(CPUBusy・GPUBusy)を記録。モバイルはAndroid vitalsの低速セッション・低速レンダリングのメトリクス - 該当する場合:普段は16.7ms前後のフレームタイムが50ms超に跳ねる瞬間が、スキル演出・大量スポーン・UI全体の更新と重なる。その瞬間もPingは変わらない - 該当しない場合:フレームタイムは安定しているのに他のキャラクターだけが一瞬止まるなら、「補間バッファがないか短い」のようなネットワーク側。跳ねる間隔が規則的なら、まず「クライアントのガベージコレクション」を確認 - 確認手段:ユーザー側の環境で確認 - 出典: - [Slow rendering](https://developer.android.com/topic/performance/vitals/render) · Android (Google) · 60FPSを出すには1フレームを16ms以内に描画する必要があり、遅れるとフレームが飛ばされてカクつき(jank)として見える - [Slow Sessions (games only)](https://developer.android.com/topic/performance/issues/slow-session) · Android (Google) · Android vitalsは、ゲームのフレームが50ms(20FPS)・34ms(30FPS)を超えると低速フレームとみなす - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime(フレーム間のCPU時間)、CPUBusy・GPUBusy(そのフレームの生成にCPU・GPUが費やした時間) #### cg-gc · クライアントのガベージコレクション · Client GC (Unity C#, Unreal, Lua) 使い終わったメモリ(ガベージ)を回収する間、ゲーム全体が止まります。一定の間隔でカクつくのが特徴です。 - なぜ → すると → 画面では:毎フレーム、一時的な文字列・配列・リストを作っては捨てる → ガベージがたまると、GCがメインスレッドを止めて回収 → 数秒〜数十秒ごとに規則的にカクつく - 症状:カクつき, フリーズ / 要因:ストール - 誰に:自分だけ / いつ:一定の周期で, 人が集中したとき - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:アロケーションの削減(文字列連結・LINQ・ラムダのキャプチャを避ける)、オブジェクトプール、インクリメンタルGCを有効のままにする(Unity 2020以降のデフォルト)、ロード画面のように止まっても問題ないタイミングで先にGCを実行。 - 数値の目安:通常は1回あたり数ms〜100ms、低スペックのスマホやメモリを多く使うゲームではそれ以上(実験では約150〜170ms)。UnityのGCは実行のたびにヒープ全体を調べるため、ゲームが使っているメモリが多いほど長くなります。 - グラフでは:周期的なスパイク(フレームタイム、GCの実行時刻) - 確認箇所:開発ビルドで、UnityプロファイラーのGC.Collect・GC.Allocマーカーを確認。Unrealはstat GCとstat Hitches(t.HitchFrameTimeThresholdで指定した時間を超えるフレームをログに記録) - 該当する場合:跳ねたフレームごとにGC.Collectの区間があり、その長さが跳ねた長さとほぼ同じで、間隔が数秒〜数十秒と規則的。人が多い場所ではフレームあたりのGC.Allocが増える - 該当しない場合:跳ねたフレームにGCの区間がなければ「メインスレッドでの同期ロード・シェーダーコンパイル」か「大人数の描画負荷」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:UnityのようにC#を使うクライアントでよく起きます。戦闘ログの文字列、ダメージの数字、UIテキストを毎フレーム新しく作るコードが主な原因です。人が多い場所でだけカクつくなら、人数に比例してガベージが増えるコードがあるということです。インクリメンタルGCは1フレームに少しずつ(Unityのデフォルトは3ms)分けて回収しますが、回収する速度よりも速くガベージを生み出すと、結局一度にまとめて止まります。Unreal Engineにも使われなくなったゲームオブジェクトを片付ける独自のGCがあり、エンジンのバージョンや設定によって異なりますが、デフォルト設定では約1分ごとに実行され、1分間隔で一瞬止まる原因になることもあります。ゲームのルールをLuaのようなスクリプトで書いたクライアントでは、そのスクリプトのGCも別に動きます。 - 出典: - [Garbage collection modes](https://docs.unity3d.com/Manual/performance-incremental-garbage-collection.html) · Unity · インクリメンタルGCがデフォルトで、複数フレームに分けて回収する。無効にするとヒープ全体を調べる間メインスレッドが止まり、数百msに達することもある - [Scripting.GarbageCollector.CollectIncremental](https://docs.unity3d.com/ScriptReference/Scripting.GarbageCollector.CollectIncremental.html) · Unity · インクリメンタルGCが1回に使う時間(タイムスライス)のデフォルトの目標は3ms(incrementalTimeSliceNanoseconds) - [Garbage Collection Settings in the Unreal Engine Project Settings](https://dev.epicgames.com/documentation/en-us/unreal-engine/garbage-collection-settings-in-the-unreal-engine-project-settings) · Epic Games · UnrealのGCが決まった間隔(Time Between Purging Pending Kill Objects、秒単位)で実行されることを示す設定。デフォルト値の数字はこの文書には記載なし - [Profiler markers reference](https://docs.unity3d.com/Manual/profiler-markers.html) · Unity · GC.Collect:ガベージコレクション中にプログラムのコードが止まった区間(1ms未満〜数百ms)、GC.Alloc:マネージヒープへのアロケーション - [Stat Commands in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/stat-commands-in-unreal-engine) · Epic Games · stat GC(ガベージコレクションの統計)、stat Hitches(t.HitchFrameTimeThresholdを超えるフレームをログに記録) #### cg-sync-load · メインスレッドでの同期ロード・シェーダーコンパイル · Synchronous asset load, shader compile 初めて見るエリア・モンスター・エフェクトを描画する直前に、ファイルの読み込みやシェーダーの生成を待って止まります。 - なぜ → すると → 画面では:新しいエリアへの進入、初めて見るスキル・装備・モンスターの登場 → メインスレッドがファイルの読み込みとシェーダーコンパイルを待つ → 初回だけ0.1〜1秒止まり、2回目以降は問題ない - 症状:フリーズ, カクつき / 要因:ストール - 誰に:自分だけ / いつ:移動中・マップ切り替え時, 特定の操作をしたとき - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:非同期ロード、事前読み込み(プリウォーミング)、シェーダーをロード画面や初回起動時に事前コンパイル、ロード画面の活用。 - 数値の目安:シェーダー1つのコンパイルに数十ms、長ければ100ms以上。テクスチャの読み込みはストレージの速度によって数十〜数百ms。 - グラフでは:接続直後・メンテ明けに急増(フレームスパイクの回数(アップデート・ドライバー更新の直後)) - 確認箇所:PresentMonで同じルートを2回記録し、初回と2回目を比較。開発ビルドでは、Unrealでr.PSOPrecache.Validationを有効にしてstat PSOPrecacheとログの「PSO PRECACHING MISS」を、UnityではプロファイラーのTimelineで跳ねたフレームのロード・シェーダーの区間を確認 - 該当する場合:初めて行く場所・初めて使うスキルでだけ0.1〜1秒跳ね、2回目には消える。アップデートやグラフィックドライバー更新の直後に報告が集中し、その後減る - 該当しない場合:同じ場所で毎回跳ねるなら、シェーダーキャッシュの問題とは別。ストレージが遅いPCでだけ移動のたびに繰り返すなら「ストレージが遅くアセットストリーミングが間に合わない」、専用GPUメモリがいっぱいなら「ビデオメモリ(VRAM)不足」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:PCでは、一度作ったシェーダーをグラフィックドライバーがシェーダーキャッシュに保存しておき、再利用します。そのため、グラフィックドライバーの更新やゲームのアップデートの直後はこのキャッシュが無効になり、問題なかった人もしばらくまたカクつきます。「アップデート後、初めて行く場所で毎回一瞬止まる」という報告が典型です。 - 出典: - [Shader loading](https://docs.unity3d.com/Manual/shader-loading.html) · Unity · シェーダーバリアントを初めて使うとき、グラフィックドライバーがGPU用に生成するため目に見えて止まることがある。一度生成したものはキャッシュされ、再び止まることはない - [Optimizing Rendering With PSO Caches in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/optimizing-rendering-with-pso-caches-in-unreal-engine) · Epic Games · パイプラインステート(PSO)を必要になった瞬間に作ると100ms以上かかることがあるため、事前に作っておく必要がある - [Direct3D 12 Return Codes](https://learn.microsoft.com/en-us/windows/win32/direct3d12/d3d12-graphics-reference-returnvalues) · Microsoft · D3D12_ERROR_DRIVER_VERSION_MISMATCH:別のドライバーバージョンで作ったPSOキャッシュは再利用できない(ドライバー更新後は再コンパイル) - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime(フレーム間のCPU時間)でフレームごとの時間を記録 - [PSO Precaching for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/pso-precaching-for-unreal-engine) · Epic Games · r.PSOPrecache.Validationを有効にすると、stat PSOPrecacheで取りこぼしたPSOの統計を確認でき、ログに「PSO PRECACHING MISS」が記録される。実行時のPSO生成がデフォルトで20msを超えるとヒッチとしてカウント #### cg-asset-stream · ストレージが遅くアセットストリーミングが間に合わない · Slow storage stalls asset streaming HDDのような遅いストレージでは、オープンワールドのテクスチャ・モデルを読み込む速度が移動に追いつかず、オブジェクトの表示が遅れたり、ゲームが読み込みを待ってカクついたりします。 - なぜ → すると → 画面では:乗り物・テレポートで高速に移動したり、人が多い場所に入ったりして、新しいテクスチャ・モデルが一気に必要になる → HDDのような遅いストレージが必要な速度で読み込めず読み込み要求がたまり、一部のロードはメインスレッドが完了まで待つ → テクスチャがしばらくぼやけ、建物・キャラクターの表示が遅れ、読み込みを待つ瞬間にカクつき・フリーズ - 症状:表示されない・ゴースト, カクつき, フリーズ / 要因:ストール - 誰に:自分だけ / いつ:移動中・マップ切り替え時, 人が集中したとき - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:外部・外部 - ゲーム開発チームの対応:読み込みをメインスレッドで待たない非同期ストリーミング、移動方向・速度に基づく先読み、低解像度(ミップマップ)・簡易モデルを先に表示して後で差し替え、高速移動中にストリーミングが遅れたら一時的に移動速度を制限するかロード画面を使用、最低・推奨スペックにSSDの要否を明記。 - 外部の対応:ユーザーにゲームをSSDにインストールするよう案内、同じディスクでダウンロードやウイルス対策ソフトのスキャンが動いていないか確認するよう案内。 - 数値の目安:Microsoftの説明によると、以前のハードディスクは1秒に数十MB、NVMe SSDは1秒に数GBを読み込み、前世代のゲームはストリーミングに1秒あたり50MB前後を使っていました。SSDの速度を前提にこれよりはるかに多く読み込むゲームをHDDで動かすと、読み込みが移動に追いつかなくなりがちです。 - グラフでは:一部だけ高い(フレームスパイクの回数(ストレージの種類別)、ディスク読み込みの遅延) - 確認箇所:Windowsのパフォーマンスモニターで、PhysicalDisk\Avg. Disk sec/Read(読み込み1回の平均時間)・Current Disk Queue LengthとPresentMonのフレームタイムを、高速で移動しながらあわせて記録。開発ビルドではUnrealのstat Streaming・stat AsyncLoad、UnityプロファイラーのAssetBundle.asset/allAssets警告(ロード完了前に結果を要求し、メインスレッドが待つ)を確認 - 該当する場合:高速で移動するとディスク読み込みの遅延とキューが急上昇し、同じ時刻にフレームが跳ねたり、テクスチャ・オブジェクトの表示が遅れたりする。同じ場面をSSDで動かすと消える - 該当しない場合:ディスクの負荷は低いのにテクスチャがぼやけるなら「ビデオメモリ(VRAM)不足」、同じ場所への2回目の訪問から問題なければ「メインスレッドでの同期ロード・シェーダーコンパイル」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:初回だけ止まって次から問題ないなら「メインスレッドでの同期ロード・シェーダーコンパイル」に近く、ストレージが遅いPCでだけ移動のたびに繰り返すならこの原因です。ビデオメモリが足りずテクスチャを退避させては読み戻す「ビデオメモリ(VRAM)不足」もテクスチャのぼやけを起こすため、ディスクの読み込み待ちとビデオメモリの使用量をあわせて確認します。ウイルス対策ソフトのリアルタイムスキャンがゲームファイルを開くたびに割り込み、読み込みがさらに遅くなることもあります。 - 出典: - [DirectStorage is coming to PC](https://devblogs.microsoft.com/directx/directstorage-is-coming-to-pc/) · Microsoft · 以前のハードディスクは毎秒数十MB、NVMe SSDは毎秒数GB、前世代のゲームのアセットストリーミング予算は毎秒50MB前後。オープンワールドゲームは移動しながら遠くの景色をリアルタイムで読み込んでは破棄する - [Texture and mesh loading](https://docs.unity3d.com/Manual/LoadingTextureandMeshData.html) · Unity · 同期アップロードはメインスレッドで1フレームのうちに読み込んで転送するため目に見える停止を生み、非同期アップロードは複数フレームにわたってストリーミングする - [Texture Streaming Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/texture-streaming-overview-for-unreal-engine) · Epic Games · ストリーマーが視点に合わせてテクスチャの解像度(ミップ)を上げ下げする。計算の大部分は非同期のワーカースレッドで行い、画面に見えるミップから読み込む - [Profiler markers reference](https://docs.unity3d.com/Manual/profiler-markers.html) · Unity · AssetBundle.asset/allAssets警告:ロード完了前に結果を要求し、メインスレッドがブロックして待つ - [Stat Commands in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/stat-commands-in-unreal-engine) · Epic Games · stat Streaming(ストリーミングテクスチャのメモリ・数)、stat AsyncLoad(非同期ロードの統計) - [Windows Performance Monitor Disk Counters Explained](https://learn.microsoft.com/en-us/archive/blogs/askcore/windows-performance-monitor-disk-counters-explained) · Microsoft · Avg. Disk sec/Readは読み込み1回の完了にかかった平均時間(I/O遅延)、Current Disk Queue Lengthは計測時点のディスクキューの長さ - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime(フレーム間のCPU時間)でフレームごとの時間を記録 - [About regular quick and full scans with Microsoft Defender Antivirus](https://learn.microsoft.com/en-us/defender-endpoint/schedule-antivirus-scans) · Microsoft · リアルタイム保護は、ファイルを開くときと閉じるときに毎回スキャンする #### cg-crowd · 大人数の描画負荷 · Render/animation cost of crowds 攻城戦やワールドボスのように数百人が1つの画面に入ると、描画のコストそのものが処理しきれなくなります。 - なぜ → すると → 画面では:1つの画面に数百人とエフェクトが重なる → アニメーション・影・名前表示・エフェクトのコストが人数に比例して増加 → FPSが60 → 15に落ち、すべての動きがカクつき、入力も遅れる - 症状:カクつき, 入力遅延 / 要因:ストール - 誰に:特定の場所・チャンネル, 自分だけ / いつ:人が集中したとき - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:距離に応じた簡略化(LOD)、表示人数の上限、エフェクト簡略化オプション、アニメーション更新頻度の引き下げ。 - 数値の目安:キャラクター1人あたり0.02〜0.1msでも、300人なら6〜30ms。60FPSのフレームバジェットは16.7msなので、これだけで予算の1/3以上を使い、多い場合は超えます。 - グラフでは:人数・負荷に連動して上昇(フレームタイム、画面内のキャラクター数) - 確認箇所:PresentMonのフレームタイムとCPUBusy・GPUBusyを、攻城戦・ワールドボスの前後で比較。開発ビルドではUnrealのstat Unit(ゲームスレッド・レンダリングスレッド・GPUの時間) - 該当する場合:画面内の人数が増えるほどフレームタイムも上がり、表示人数の制限やエフェクト簡略化オプションを有効にするとすぐに改善する - 該当しない場合:人数と関係なく跳ねるなら「フレームタイムのスパイク」か「クライアントのガベージコレクション」。FPSは問題ないのに他の人の動きだけ遅れるなら「メインスレッドのパケット処理ボトルネック」 - 確認手段:ユーザー側の環境で確認 - 出典: - [Introduction to level of detail](https://docs.unity3d.com/Manual/LevelOfDetail.html) · Unity · LODがないと、画面上で小さく見える物体も同じ複雑さで描画する。LODで描画負荷を減らす - [Animation Budget Allocator in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/animation-budget-allocator-in-unreal-engine) · Epic Games · スケルタルメッシュのアニメーション更新(ティック)を動的に減らし、アニメーションにかける時間を予算内に抑える - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime・CPUBusy・GPUBusyで、CPUとGPUのどちらがフレームを遅らせているかを区別 - [Stat Commands in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/stat-commands-in-unreal-engine) · Epic Games · stat Unit:フレーム全体の時間と、ゲームスレッド・レンダリングスレッド・GPUの時間 #### cg-net-mainthread · メインスレッドのパケット処理ボトルネック · Network processing on the main thread 受信したパケットをフレームごとに決まった量しか処理しないと、押し寄せたパケットがどんどん次のフレームへ持ち越されます。 - なぜ → すると → 画面では:人が多い場所で、毎秒数千件の更新が届く → メインスレッドがフレームあたりの処理量の上限に達し、読み切れない → 他の人の動きがだんだん遅れ、まとめて反映される - 症状:早送り, 入力遅延 / 要因:ストール, 遅延 - 誰に:特定の場所・チャンネル, 自分だけ / いつ:人が集中したとき - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:受信・解析は別スレッドで行い、同じ対象の古い位置更新はまとめて最新のものだけを適用。サーバー:人が多い場所では遠くのキャラクターの更新を送る頻度を下げて送信量を削減。 - 数値の目安:処理しきれないパケットがたまると、数秒で1秒分の遅れになります。 - グラフでは:人数・負荷に連動して上昇(未処理の受信パケット数、受信〜適用の遅延) - 確認箇所:クライアントがフレームごとに処理しきれず残したパケット数と、パケットの到着からゲームに適用するまでの遅延をログに記録し、周囲の人数とあわせて確認 - 該当する場合:人が多い場所で、残ったパケット数と適用までの遅延が増え続け、同じ時刻のPingとサーバーの送信間隔は正常 - 該当しない場合:適用までの遅延はないのにパケット自体の到着が遅いならネットワーク区間。フレームタイムが大きく上がるなら「大人数の描画負荷」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · 帯域幅が足りないときはすべてのアクターを毎回レプリケートせず、見ている人との距離や最後のレプリケーションからの経過時間で優先度を付ける - [Replication Graph in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/replication-graph-in-unreal-engine) · Epic Games · 接続者やレプリケーション対象が多いゲーム(MMORPGなど)は、位置ごとにまとめて必要な対象だけを送らないと、サーバーCPUのボトルネックを避けられない #### cg-no-buffer · 補間バッファがないか短い · Missing/short interpolation buffer サーバーのパケットを受け取ってすぐ描画すると、ジッター(到着間隔のばらつき)がそのまま画面に表れます。 - なぜ → すると → 画面では:受信した位置をすぐに描画するか、バッファがジッターより短い → 遅れて届いたパケットの分だけ止まり、まとめて届いたパケットの分だけ跳ぶ → 他のキャラクターの動きがカクつく - 症状:カクつき / 要因:ジッター - 誰に:自分だけ / いつ:常に - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:補間バッファを設け、回線の状態に応じてバッファの長さを自動調整。サーバー:バッファの分だけ過去の姿を見て撃っても判定が合うよう、ラグコンペンセーション(巻き戻し判定)を適用。 - 数値の目安:通常、サーバーがパケットを送る間隔の2倍程度(1秒に20回受信するなら100ms)をバッファとして持ちます。 - グラフでは:最初から常に高い(パケットの到着間隔、補間バッファが空になった回数) - 確認箇所:クライアントで、サーバーパケットの到着間隔の分布と、補間する次のスナップショットがなくて止まったり外挿に切り替わったりしたフレーム数を記録 - 該当する場合:到着間隔のばらつきが補間バッファの長さを超えることが多く、そのたびにバッファが空になって他のキャラクターが一瞬止まる。バッファを長くすると減る - 該当しない場合:バッファが十分なのに一瞬止まるなら、サーバーの送信間隔そのものが不規則でないか(ティックの遅延)を確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:バッファを長くすると滑らかになりますが、その分だけ相手の過去の姿を見ることになります。そのため攻撃の当たり判定では、サーバーが「その人が見ていた過去」まで巻き戻して確認するラグコンペンセーションを併用します。 - 出典: - [Interpolation and extrapolation (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/interpolation.html) · Unity · 遅れて届くパケットを待つためにわざと遅らせて描画するバッファ補間。バッファが大きいほど正確だが、その分遅延が増える - [Struct ClientTickRate (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/api/Unity.NetCode.ClientTickRate.html) · Unity · 補間バッファのデフォルト値はInterpolationTimeNetTicks = 2(サーバーの送信2回分) - [Physics (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/physics.html) · Unity · ラグコンペンセーション:クライアントがそのティックで見ていた衝突判定用のワールドをサーバーが探し、命中したかどうかを判定 #### cg-extrap · 過度な外挿(デッドレコニング) · Over-extrapolation / dead reckoning パケットが届かない間は最後の速度のまま動かし続けて見せ、外れていたと分かると元に戻します。 - なぜ → すると → 画面では:パケットの受信が途切れ、最後の方向・速度のまま移動させ続ける → 実際には相手は止まっていたか、方向を変えていた → 相手キャラクターがしばらく進んでから本当の位置へ一気に移されたり、壁をすり抜けたりする。パケットの到着間隔がばらつくと、先に進んでは戻されるのを繰り返し、震えるように動く - 症状:ワープ, カクつき / 要因:パケットロス, ジッター - 誰に:自分だけ / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:外挿時間の上限(例:200〜250ms)、外れたときは滑らかに収束させる。 - 数値の目安:速度6m/sなら、300ms外れるだけで1.8mずれます。 - グラフでは:不定期なスパイク(外挿時間、位置補正の距離) - 確認箇所:他のキャラクターを外挿で描画した時間と、新しいパケットが届いた後に位置を修正した距離を記録 - 該当する場合:パケットが途切れた区間ごとに外挿時間が上限なく延び、その後の補正距離が数mに達する - 該当しない場合:外挿を短く打ち切っているのにワープするなら、パケットロス・遅延そのものが大きいため、回線・経路側を確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Interpolation and extrapolation (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/interpolation.html) · Unity · 次のスナップショットが時間どおりに届かないとき、同じ方向・速度で動かし続ける外挿は外れやすいため上限を設ける(Unityのデフォルトは20ティック、60Hzで約1/3秒) - [Peeking into VALORANT's Netcode](https://technology.riotgames.com/news/peeking-valorants-netcode) · Riot Games · 遅れたり欠けたりしたデータを推測で埋めて外れると、サーバーとずれてキャラクターが跳んだり、滑るように補正されたりする #### cg-predict · クライアントサイド予測の不一致 · Prediction mismatch / reconciliation 自分のクライアントが先に動かして見せたのに、サーバーの計算結果が違うと、自分のキャラクターが引き戻されます。 - なぜ → すると → 画面では:クライアントがサーバーの確認前に先に動く(予測) → サーバーが衝突・移動速度・バフを違う形で計算するか、コマンドを受け取れない → 確認が届いたとき、自分のキャラクターが後ろへ引き戻される - 症状:引き戻し / 要因:パケットロス, 遅延 - 誰に:自分だけ / いつ:移動中・マップ切り替え時, ときどきランダムに - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:サーバーと同じ移動コードを使用、入力の重複送信、補正は滑らかに。サーバー:クライアントと同じ移動コードを使用、重複して届いた入力は入力番号で除外して1回だけ処理。 - 数値の目安:引き戻される距離は「ずれた時間 × 移動速度」。コマンドがいくつか消えるだけで1〜3m。 - グラフでは:不定期なスパイク(サーバー補正(予測ミス)の回数) - 確認箇所:サーバーが送った位置補正の回数と補正距離を記録。UnrealではサーバーのClientAdjustPositionによる補正、UnityのNetcode for Entitiesでは予測ミスで巻き戻して再計算した回数を数える - 該当する場合:引き戻しの報告時刻に補正が集中し、特定のバフ・地形・移動スキルで補正距離が繰り返し大きくなる - 該当しない場合:補正がパケットロスの多いときにだけ集中するなら、入力パケットのロス(回線)側。補正がないのに他のキャラクターだけ引き戻されて見えるなら「過度な外挿(デッドレコニング)」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Introduction to prediction (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/intro-to-prediction.html) · Unity · クライアントとサーバーが同じシミュレーションコードで予測し、サーバーの状態と異なれば(予測ミス)巻き戻して再計算するため、補正が目に見える - [Use the command stream to handle user inputs (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/command-stream.html) · Unity · パケットロスに備え、最新の入力と一緒に直前数ティック分の入力を重複して送る - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · クライアントが先に動き、サーバーが同じ移動を再現。位置が異なればClientAdjustPositionで補正し、保存しておいた移動を再適用 #### cg-fixed-step · 固定タイムステップの追いつき処理の暴走 · Fixed-timestep catch-up / spiral of death 一度止まった後、遅れた計算をまとめて処理しようとして、その計算のせいでさらに遅れます。 - なぜ → すると → 画面では:ゲームのシミュレーションを固定間隔で回している最中に、一度止まる → 遅れたステップを1フレームでまとめて計算 → 長いフレームが次々と続いて跳ねるか、上限に達して世界全体がスローモーションになる - 症状:カクつき, 早送り, スローモーション / 要因:ストール - 誰に:自分だけ / いつ:ときどきランダムに, 人が集中したとき - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:フレームあたりの追いつき処理の上限、余った時間は補間で処理。 - グラフでは:不定期なスパイク(フレームタイム、フレームあたりの固定ステップ数) - 確認箇所:開発ビルドのプロファイラーで、1フレームに固定ステップが何回実行されたか(UnityではFixedBehaviourUpdateなどFixedUpdate段階のマーカー数)とフレームタイムをあわせて確認 - 該当する場合:長いフレーム1つの後に、ステップを何回も回す長いフレームが次々と続く。上限(UnityのMaximum Allowed Timestep)に達すると、ゲーム内の時間が実際より遅く流れる - 該当しない場合:長いフレームが1回で終わるなら「フレームタイムのスパイク」か「クライアントのガベージコレクション」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:Unityの物理演算(FixedUpdate)が代表的な固定ステップです(デフォルト0.02秒、1秒に50回)。Time設定のMaximum Allowed Timestep(1フレームで追いつく最大時間、デフォルト約0.33秒)が追いつき処理の上限です。1フレームがこれより長いと、あふれた時間は捨てられ、その分ゲームの時計が実際より遅れます。 - 出典: - [Set fixed timestep to optimize physics simulation frequency](https://docs.unity3d.com/Manual/physics-optimization-cpu-frequency.html) · Unity · Fixed Timestepのデフォルト値は0.02秒(1秒に50回)。フレームが長いと1フレームで物理ステップを何回も回すため負荷が増える - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · Maximum Allowed Timestepのデフォルト値は1/3秒(0.3333333)。1秒止まってもゲーム内の時間は0.333秒しか進まない。追いつき処理のステップがさらに遅くなる悪循環を防ぐための上限 - [Profiler markers reference](https://docs.unity3d.com/Manual/profiler-markers.html) · Unity · FixedBehaviourUpdate:MonoBehaviour.FixedUpdateの実行区間。物理のマーカーはFixedUpdate段階で呼ばれる #### cg-clock · 時刻同期の誤差 · Clock sync error クライアントが推定したサーバー時刻がずれていると、補間のタイミングやクールタイムの判定がずれます。 - なぜ → すると → 画面では:接続時に一度だけサーバー時刻を合わせ、Pingが変わってもそのまま → 補間するタイミング・クールタイムが終わる時刻がサーバーとずれる → 相手がときどき一瞬止まる。クールタイムが終わったのにスキルが拒否される - 症状:カクつき, 不発・ロールバック / 要因:遅延 - 誰に:自分だけ / いつ:長時間稼働するほど, ときどきランダムに - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:定期的な時刻同期(往復時間を測って補正)、急に変えず徐々に合わせる、経過時間はPCの時刻の代わりにモノトニッククロック(monotonic clock)で計測。 - グラフでは:徐々に上昇(推定サーバー時刻の誤差) - 確認箇所:クライアントが推定したサーバー時刻と、サーバーがパケットに入れて送ったサーバー時刻(ティック番号)の差を定期的に記録 - 該当する場合:誤差が接続後の時間経過とともに大きくなるか、PCの時計が合わせられた瞬間に一気に跳ね、その頃にスキルの拒否や一瞬止まるという報告が増える - 該当しない場合:誤差が小さいままなのにスキルが拒否されるなら、サーバーの判定・遅延側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:経過時間をPCの日付・時刻(wall clock)で測ると、Windowsがインターネット時刻に合わせて時計を修正したり、ユーザーが時計を変更したりした瞬間に、ゲームの時刻が跳ねます。経過時間は巻き戻らないモノトニッククロック(monotonic clock、Stopwatchなど)で測る必要があります。 - 出典: - [RFC 5905: Network Time Protocol Version 4: Protocol and Algorithms Specification](https://www.rfc-editor.org/rfc/rfc5905) · IETF · リクエスト・レスポンスの4つの時刻から、往復遅延と時計のずれを計算する方式 - [Acquiring high-resolution time stamps](https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps) · Microsoft · QueryPerformanceCounter(Stopwatchが使用)は外部の時刻と同期しない経過時間用の時計。UTC時刻が必要なときだけシステム時刻を使う - [Time synchronization (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/time-synchronization.html) · Unity · 往復時間からサーバー時刻を推定し、時刻を大きく変えずに進行速度を少しずつ調整して合わせる #### cg-float-time · float型で持つ時刻の精度低下 · Float time precision loss on long sessions ゲーム内の時刻を精度の低い小数形式(float)で持っていると、起動したままの時間が長いほど時間分解能(区別できる最小の時間差)が下がり、動きやエフェクトが震えます。 - なぜ → すると → 画面では:ゲーム起動後の経過時間をfloatに積算するか、そのままシェーダーに渡す → 起動したままの時間が長いほど、floatで表せる最小の差が大きくなる → 何日も起動したままのクライアントでだけ、キャラクター・アニメーション・流れるエフェクトが小刻みに震え、再起動すると直る - 症状:カクつき / 要因:ジッター - 誰に:自分だけ / いつ:長時間稼働するほど - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:経過時間はdouble(64ビット)か整数で保持、シェーダーに渡す時間は一定周期でリセット、数日間にわたる長時間の自動テスト。 - 数値の目安:32ビットのfloatは有効数字が7桁ほどしかないため、起動から1日(約86,400秒)で時間分解能が約8msとなり60FPSの1フレーム(16.7ms)の半分ほど、1週間で約60msとなり1フレームより大きくなります。 - グラフでは:徐々に上昇(連続起動時間別の震えの報告) - 確認箇所:震えの報告と一緒にクライアントの連続起動時間を受け取り、再起動の前後を比較。開発側では、ゲーム開始時刻の値を数日経過した値にして動かすテスト - 該当する場合:何日も起動したままのクライアントでだけ震え、再起動すると消える。起動したままの時間が長いほどひどくなる - 該当しない場合:起動直後から震えるなら「補間バッファがないか短い」か「タイマー分解能」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:オート狩りをオンにしたまま何日も終了しないモバイルMMOで特によく起きます。UnityのTime.timeもfloatなので、Unityはdouble型のTime.timeAsDoubleを別に用意し、こちらを推奨しています。 - 出典: - [Time.timeAsDouble](https://docs.unity3d.com/ScriptReference/Time-timeAsDouble.html) · Unity · Time.timeのdouble版。長く起動するほどfloatより精度が高く、ほとんどの場合こちらを推奨 - [Floating-point numeric types (C# reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types) · Microsoft · floatの精度は約6〜9桁、doubleは約15〜17桁 #### cg-vsync · V-Syncとレンダーキュー · V-Sync, render queue GPUが描画したフレームを何枚かキューにためておき、モニターの周期に合わせて出力する間、入力が遅れます。 - なぜ → すると → 画面では:グラフィックドライバーがフレームを1〜3枚先にキューにためる → 入力が画面に反映されるまで、その分余計に時間がかかる → Pingは低いのに、操作が重くもたつく - 症状:入力遅延, カクつき / 要因:遅延 - 誰に:自分だけ / いつ:常に - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:外部・外部 - ゲーム開発チームの対応:低遅延モードへの対応、フレームキューを減らす、リフレッシュレートより少し低く設定するフレームレート上限オプションの提供、スマホではフレームペーシング機能を有効化。 - 外部の対応:ユーザーに、可変リフレッシュレート対応モニターと組み合わせてリフレッシュレートより少し低いフレームレート上限を設定すること、グラフィックドライバーの低遅延モードを有効にすることを案内。 - 数値の目安:60Hzでは1枚あたり16.7ms。CPUがGPUや画面の周期より速く、キューの3枚(DirectX 11のデフォルト)が埋まると、50msが上乗せされます。ダブルバッファのV-Syncでは、17msかかったフレームが次の画面更新タイミング(33.3ms)まで待ち、その間は前のフレームがもう一度表示されます。 - グラフでは:最初から常に高い(入力〜画面の遅延) - 確認箇所:PresentMonのMsClickToPhotonLatency・MsAllInputToPhotonLatency(マウス・キーボードの入力から画面への出力まで)とDisplayLatencyを、V-Sync・低遅延モード・フレームレート上限を切り替えながら比較。MsPCLatency(PCが入力を受けてから画面に送るまで)は、ゲームがPC Latencyイベントを出力する場合にだけ記録される - 該当する場合:V-Syncを有効にしたときやフレームレート上限がないときにこの遅延が1〜2フレーム(数十ms)増え、低遅延モードやリフレッシュレートより少し低いフレームレート上限では減る。Pingは変わらない - 該当しない場合:PC内の遅延は低いのに操作が遅れるなら「ディスプレイ・入力デバイス・フレーム生成による遅延」、Pingが高いならネットワーク側 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:V-Sync(垂直同期)は、モニターが画面を切り替える瞬間にだけ新しいフレームを出力する設定です。ティアリングはなくなりますが、その瞬間を待つ分だけ入力が遅れ、FPSが60を下回ると60と30の間を行き来してカクつきます。可変リフレッシュレート(VRR)対応モニターは、フレームの準備ができたタイミングに合わせて画面を切り替えるため、この待ち時間を減らせます。スマホでも同じことが起きます。30FPSのゲームが60Hzの画面にフレームを均等に出力できないと、平均は30FPSなのに1枚あたりの表示時間が49・16・33msのようにばらつき、カクつきます(Androidの開発者向けドキュメントの例)。Androidのフレームペーシング(フレームの出力間隔をそろえる)ライブラリや、エンジンの同様のオプションで軽減します。 - 出典: - [IDXGIDevice1::SetMaximumFrameLatency](https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgidevice1-setmaximumframelatency) · Microsoft · ドライバーがキューにためられるフレーム数のデフォルト値は3(1〜16) - [Reduce latency with DXGI 1.3 swap chains](https://learn.microsoft.com/en-us/windows/uwp/gaming/reduce-latency-with-dxgi-1-3-swap-chains) · Microsoft · キューが空くまでPresentがブロックされ、描画から表示までほぼ1フレーム余計に待つことになる。待機可能なスワップチェーンで減らす - [Frame Pacing library](https://developer.android.com/games/sdk/frame-pacing) · Android (Google) · 60Hzの画面で新しいフレームがなければ前のフレームを再表示する。30FPSのゲームのフレームタイムが49・16・33msのようにばらつく例 - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsPCLatency(PCが入力を受けてから画面に送るまで)、MsClickToPhotonLatency(マウスクリック〜画面)、MsAllInputToPhotonLatency(キーボード・マウス入力〜画面)、DisplayLatency(フレームの提出〜モニターへの出力) - [PresentMon Console Application (README-ConsoleApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-ConsoleApplication.md) · Intel · MsPCLatencyは、アプリがPC Latencyイベントを出力しないと記録されない(--track_pc_latency)。MsAllInputToPhotonLatencyはキーボード・マウス入力が基準 #### cg-leak · クライアントのメモリリーク · Client memory leak 長く起動しておくほどメモリが増えてだんだん重くなり、最後にはゲームが強制終了します。 - なぜ → すると → 画面では:エリアを行き来するたびに、テクスチャ・UI・エフェクトが解放されずに残る → GCが頻繁になり、OSのメモリが足りなくなってスワップが発生 → 数時間プレイした後、だんだんカクつくようになり、強制終了(プレイヤーには切断のように見える) - 症状:カクつき, 切断 / 要因:ストール - 誰に:自分だけ / いつ:長時間稼働するほど - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:エリア切り替え時にメモリ使用量を計測、解放されないテクスチャ・UI・エフェクトを見つけて修正、長時間の自動テスト(ソークテスト)。 - グラフでは:徐々に上昇(ゲームプロセスのメモリ) - 確認箇所:パフォーマンスモニターでProcess(ゲーム)\Private Bytesを数時間記録。モバイルはAndroidのApplicationExitInfoの終了理由(REASON_LOW_MEMORY)とiOSのjetsamレポート - 該当する場合:エリアを行き来するたびにメモリが上がって下がらず、起動したままの時間が長いほどカクつき・強制終了が増える - 該当しない場合:メモリは一定なのに、長く起動しておくほど震えるだけなら「float型で持つ時刻の精度低下」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:スマホは主にメモリを圧縮してしのぎます。それでもメモリが足りなくなると、OSがゲームをすぐに終了させます(落ちる)。RAMが少ない端末ほど先に終了させられます。 - 出典: - [Memory allocation among processes](https://developer.android.com/topic/performance/memory-management) · Android (Google) · AndroidはメモリをzRAMに圧縮してしのぎ、足りなくなると低メモリキラーがプロセスを終了させる。フォアグラウンドのアプリが終了するとクラッシュのように見える - [Identifying high-memory use with jetsam event reports](https://developer.apple.com/documentation/xcode/identifying-high-memory-use-with-jetsam-event-reports) · Apple · iOSはメモリ逼迫が解消しないとアプリを強制終了(jetsam)し、アプリごとのメモリ上限を超えると終了の対象になる - [Find user-mode memory leaks with Performance Monitor (PerfMon)](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/using-performance-monitor-to-find-a-user-mode-memory-leak) · Microsoft · Process > Private Bytes(プロセスが割り当てた専用メモリ)・Virtual Bytesを長時間記録し、増える一方ならリーク - [ApplicationExitInfo](https://developer.android.com/reference/android/app/ApplicationExitInfo) · Android (Google) · REASON_LOW_MEMORY:システムの低メモリキラーがアプリのプロセスを終了させた(非対応の端末ではREASON_SIGNALED・SIGKILLとして報告) #### cg-crash · クライアントのクラッシュ · Client crash 処理されないエラーでゲームが終了します。プレイヤーには切断のように見えますが、サーバーは正常です。 - なぜ → すると → 画面では:null参照、メモリ不足、グラフィックドライバーのエラー → ゲームプロセスが強制終了 → 「落ちた」という報告。同じ時刻、ほかの人は問題ない - 症状:切断 / 要因:ストール - 誰に:自分だけ / いつ:特定の操作をしたとき, ときどきランダムに - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:外部・外部 - ゲーム開発チームの対応:クラッシュレポートの収集、端末・ドライバー別の統計、件数の多いエラーから修正。 - 外部の対応:特定のグラフィックドライバーのバージョンに集中していれば、ユーザーにドライバーの更新を案内。 - グラフでは:一部だけ高い(クラッシュ数(端末・グラフィックドライバー・ビルド別)) - 確認箇所:クラッシュレポートとAndroid vitalsのクラッシュ率を、端末・ドライバー・ビルド別に確認。ユーザーのPCでは、イベントビューアーのアプリケーションログにあるイベントID 1000(障害が発生しているモジュールの名前)と「Display driver stopped responding and has recovered」の記録 - 該当する場合:切断の報告時刻にクラッシュの記録があり、同じ時刻に同じサーバーのほかのユーザーは正常。特定の端末・ドライバーのバージョン・モジュールに集中 - 該当しない場合:クラッシュの記録がなく接続だけ切れたなら「NATマッピングの期限切れ」か回線側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Crashes](https://developer.android.com/topic/performance/vitals/crash) · Android (Google) · 処理されない例外やシグナル(SIGSEGVなど)でアプリが予期せず終了するのがクラッシュ。Play ConsoleのAndroid vitalsで集計 - [WDDM Support for Timeout Detection and Recovery (TDR)](https://learn.microsoft.com/en-us/windows-hardware/drivers/display/timeout-detection-and-recovery) · Microsoft · GPUがデフォルトで2秒以内に処理を終えられないと、WindowsがグラフィックドライバーとGPUをリセット - [The application or service crashing behavior troubleshooting guidance](https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/troubleshoot-application-service-crashing-behavior) · Microsoft · アプリケーションログのイベントID 1000が実際のクラッシュの記録で、障害が発生しているアプリケーション・モジュールの名前(Faulting module name)を含む #### cg-anticheat · ゲームのセキュリティモジュール(アンチチート)の検査 · Anti-cheat scan and heartbeat チートを防ぐためにゲームと一緒に動くセキュリティモジュールが、定期的に検査を行います。検査が重かったり、セキュリティサーバーとやり取りするハートビート(定期的な生存確認の信号)が遅れたりすると、カクついたり切断されたりします。 - なぜ → すると → 画面では:セキュリティモジュールが定期的に、ゲームのメモリ・実行中のプログラム・ドライバーを検査 → 検査の間ゲームスレッドが止まるか、ハートビートが時間どおりに届かない → 一定の間隔で一瞬止まり、ひどいとセキュリティエラーの案内とともに切断 - 症状:カクつき, フリーズ, 切断 / 要因:ストール - 誰に:自分だけ / いつ:一定の周期で, 接続直後・メンテ明け, ときどきランダムに - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:重い検査はゲームスレッドの外で少しずつ分けて実行、セキュリティモジュールのバージョン別にカクつきや落ちた件数の統計を比較(アップデート直後に集中していればセキュリティモジュールのベンダーに連絡)。サーバー:ハートビートが1〜2回遅れる程度は許容。 - 数値の目安:軽い検査は通常1msもかかりませんが、ゲームスレッドで動く重い検査は、実装によっては1回で数十〜数百msを占めることもあります。 - グラフでは:周期的なスパイク(フレームタイム、アンチチートによるキック数) - 確認箇所:PresentMonのフレームタイムで跳ねる間隔を測り、サーバーが受け取ったアンチチートのキック理由(EOSではClientActionReasonのAuthenticationFailed / Authentication Timed Outなど)を、セキュリティモジュールのバージョン・スペック別に集計 - 該当する場合:ゲームの状況と関係なく一定間隔の短い停止が繰り返され、セキュリティモジュールのアップデート直後に、特定のスペックでカクつき・認証タイムアウトによるキックが増える - 該当しない場合:セキュリティモジュールのバージョンと関係なく、すべてのスペックで同じ間隔なら「クライアントのガベージコレクション」か「バックグラウンドプロセスによるCPU占有」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:セキュリティモジュールはドライバーとしてOSの深い部分に入り込んでいるため、ウイルス対策ソフト・オーバーレイ・ほかのゲームのセキュリティモジュールと衝突することもあります。セキュリティモジュールのアップデート直後に、特定のスペックでだけカクつく・落ちるという報告が集中したら、まずこれを疑います。 - 出典: - [Using the Anti-Cheat Interfaces](https://dev.epicgames.com/docs/epic-online-services/trust-and-safety/anti-cheat-interfaces/using-anti-cheat) · Epic Games · サーバーが決まった時間(RegisterTimeout)内にクライアントのアンチチートメッセージを受け取れないと、認証タイムアウトとしてキックする(ロードで止まったクライアントがよくある原因)。最近のモジュール更新が問題なら以前のモジュールに戻す - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime(フレーム間のCPU時間)でフレームごとの時間を記録 ### L2 クライアントのOS・端末(原因15件) #### co-background · バックグラウンドプロセスによるCPU占有 · Background CPU contention ウイルス対策ソフトのスキャン、Windows Update、配信ソフト、ブラウザの動画がコアを占有すると、ゲームスレッドがCPUを割り当ててもらえずに待たされます。 - なぜ → すると → 画面では:他のプログラムがCPUコアを長時間占有 → ゲームスレッドがスケジューリング待ちになる → フレームが遅れ、受信したパケットの処理も遅れる - 症状:カクつき, 早送り / 要因:ストール - 誰に:自分だけ / いつ:ときどきランダムに, 一定の周期で - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:ゲームスレッドの優先度の調整、カクつきが起きたときのログにPC全体のCPU使用率も記録して、他のプログラムが原因かどうかを区別。 - 外部の対応:ユーザーに、Windowsのゲームモードを有効にすること、プレイ中は不要なプログラム(ウイルス対策ソフトのスキャン・Windows Update・配信ソフト・ブラウザの動画)を終了することを案内。 - 数値の目安:Windowsは通常、1回に数ms〜数十msずつコアを割り当てます。スケジューリングで一度後回しにされるだけで、1フレームが消えます。 - グラフでは:不定期なスパイク(PC全体のCPU使用率、フレームタイム) - 確認箇所:タスクマネージャーのプロセスタブのCPU列と、パフォーマンスモニターのProcessor Information(_Total)\% Processor Timeを、PresentMonのフレームタイムとあわせて記録。ウイルス対策ソフトが疑わしければ、New-MpPerformanceRecordingで記録し、Get-MpPerformanceReportでスキャン時間の長いファイル・プロセスを確認 - 該当する場合:カクついた時刻に他のプログラム(ウイルス対策ソフトのスキャン・アップデート・配信ソフト)のCPU使用率が急上昇するか、ゲームフォルダーのファイルがスキャン時間の上位に入る。そのプログラムを終了するか除外に設定すると消える - 該当しない場合:CPU使用率が低いのに画面全体が一瞬止まり、音にノイズが入るなら「NICの省電力・ドライバーの問題」(DPC遅延) - 確認手段:ユーザー側の環境で確認 - もっと詳しく:Windowsは前面に表示しているウィンドウ(フォアグラウンド)のプログラムに少し高い優先度を与えますが、コアの数より実行すべき処理が多ければ、ゲームも待たされます。ウイルス対策ソフトは、CPUを使うことよりも「リアルタイム監視」で割り込むことのほうが多いです。ゲームがファイルを開くたびにスキャンするため、アセットを読み込む瞬間の停止が長くなります。 - 出典: - [Multitasking](https://learn.microsoft.com/en-us/windows/win32/procthread/multitasking) · Microsoft · Windowsはスレッドごとにタイムスライスを与え、使い切ると次のスレッドに切り替える。タイムスライスは約20ms(OS・CPUによって異なる) - [Priority Boosts](https://learn.microsoft.com/en-us/windows/win32/procthread/priority-boosts) · Microsoft · 前面に表示しているウィンドウ(フォアグラウンド)のプロセスは、優先度をバックグラウンドプロセス以上に引き上げる - [About regular quick and full scans with Microsoft Defender Antivirus](https://learn.microsoft.com/en-us/defender-endpoint/schedule-antivirus-scans) · Microsoft · リアルタイム保護は、ファイルを開くときと閉じるとき、フォルダーを開くときに毎回スキャンする - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · Processor Information:% Processor Timeカウンター - [Performance analyzer for Microsoft Defender Antivirus](https://learn.microsoft.com/en-us/defender-endpoint/tune-performance-defender-antivirus) · Microsoft · New-MpPerformanceRecordingで記録し、Get-MpPerformanceReportでスキャン時間に影響した上位のファイル・パス・プロセスを確認 - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime(フレーム間のCPU時間)でフレームごとの時間を記録 #### co-power · 省電力モード・サーマルスロットリング · Power saving, thermal throttling ノートPCのバッテリーモード、スマホの省電力モード、端末の発熱によって、CPU・GPUの速度が落ちます。発熱の場合は、最初は問題なく、しばらくしてから重くなるのが特徴です。 - なぜ → すると → 画面では:バッテリー・省電力モードか、端末が熱くなっている → CPU・GPUのクロックを、端末によって30〜50%下げる → 省電力モードは起動直後から、発熱は数分〜20分ほどプレイした後から、FPSが下がってカクつく - 症状:カクつき, 入力遅延 / 要因:ストール - 誰に:自分だけ / いつ:長時間稼働するほど, 常に - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:グラフィックオプションの自動調整、フレームレート上限による発熱管理、OSが通知する発熱段階(iOSのthermalState、Androidの発熱状態API)を見て先にオプションを下げる、GPUを2つ搭載したノートPCでディスクリートGPUを使うよう実行ファイルで指定(NvOptimusEnablement・AmdPowerXpressRequestHighPerformanceのエクスポート)。 - 外部の対応:ユーザーに、省電力モードを解除すること、ノートPCは電源につなぐことを案内。「いいノートPCなのにFPSが低い」という報告なら、ゲームがどちらのGPUで動いているかを確認し、Windowsのグラフィックの設定でゲームを高パフォーマンスのGPUに指定するよう案内。 - グラフでは:徐々に上昇(FPS、CPU・GPUのクロック) - 確認箇所:PresentMonのCPUFrequency・GPUFrequency・CPUTemperature・GPUTemperatureをフレームタイムとあわせて20〜30分記録し、タスクマネージャーのプロセスタブのGPUエンジン列で、ゲームがどちらのGPUで動いているかを確認。モバイルは、Androidの発熱API(getThermalHeadroom、発熱状態)とiOSのthermalStateをFPSとあわせて記録 - 該当する場合:温度が上がってクロックが下がった時点からFPSが落ちるか、バッテリー・省電力モードのときだけクロックが低い。または、ゲームが内蔵GPUで動いている - 該当しない場合:クロック・温度が変わらないのにFPSが落ちるなら「バックグラウンドプロセスによるCPU占有」か「クライアントのメモリリーク」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:GPUを2つ搭載したノートPCは、電力を節約するためにゲームを遅い内蔵GPUで動かすことがあります。「いいノートPCなのにFPSが低い」という報告なら、まずゲームがどちらのGPUで動いているかを確認します。 - 出典: - [Thermal API](https://developer.android.com/games/optimize/adpf/thermal) · Android (Google) · 端末は高い性能を限られた時間しか維持できず、その後は発熱でスロットリングされる。発熱状態を見て、先に負荷を下げることを推奨 - [thermalState](https://developer.apple.com/documentation/foundation/processinfo/thermalstate-swift.property) · Apple · iOSが通知する現在の発熱段階。段階が上がったら、アプリはリソースの使用を減らす必要がある - [Selecting the Best Graphics Device to Run a 3D Intensive Application](https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/) · AMD · GPUを2つ搭載したノートPCで内蔵GPUで動くと、60FPSのゲームが30FPSになることもある。AmdPowerXpressRequestHighPerformanceのエクスポートでディスクリートGPUを選択 - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · CPUFrequency・GPUFrequency(クロック)、CPUTemperature・GPUTemperature(温度)をフレームごとに記録 - [GPUs in the task manager](https://devblogs.microsoft.com/directx/gpus-in-the-task-manager/) · Microsoft · タスクマネージャーには、プロセスごとのGPU使用率と、その値がどのGPU・エンジンのものかを示す列がある #### co-timer · タイマー分解能 · Timer resolution (Windows 15.6ms) Windowsのデフォルトのタイマーは15.6ms単位なので、「1msだけ待つ」が実際には次のタイマー周期まで、長いと15.6msまで延びます。 - なぜ → すると → 画面では:フレームレート制限・パケット送信をSleep(短い待機)で実装 → OSが15.6ms単位でしか起こしてくれない → フレーム間隔と入力の送信間隔がばらつく - 症状:カクつき / 要因:ジッター - 誰に:自分だけ / いつ:常に - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:高分解能タイマーの使用、Sleepで待つ代わりにイベント・V-Syncを基準にしたペーシング。 - 数値の目安:15.6ms単位では16.7msの間隔に合わせられず、フレーム間隔が15.6msと31.2msの間を行き来します。 - グラフでは:最初から常に高い(フレーム間隔の分布) - 確認箇所:PresentMonのMsBetweenPresents(フレーム間隔)の分布と、powercfg /energyレポートの「Platform Timer Resolution」項目(タイマー分解能を変更したプロセス)を確認 - 該当する場合:フレーム間隔が15.6msと31.2msのように15.6msの倍数に集中し、ゲームがより高いタイマー分解能を要求していない - 該当しない場合:間隔が均等に散らばっているなら、タイマーよりも「バックグラウンドプロセスによるCPU占有」やフレーム負荷側 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:以前のWindowsでは、1つのプログラムがタイマーを1msに変更すると、すべてのプログラムに適用されていました。そのため「ブラウザを開いておくとゲームが滑らかになる」という話もありました。Windows 10 バージョン2004からは要求したプログラムにだけ適用され、Windows 11では、最小化されたり完全に隠れていて音も出していないウィンドウの要求を適用しないことがあります。 - 出典: - [_WDF_TIMER_CONFIG (wdftimer.h)](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdftimer/ns-wdftimer-_wdf_timer_config) · Microsoft · 通常のタイマーの精度はシステムクロックのティック間隔であるデフォルト15.6ms、高分解能タイマーは1ms - [timeBeginPeriod function (timeapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod) · Microsoft · Windows 10 バージョン2004より前はグローバル設定、それ以降は要求したプロセスにだけ適用。Windows 11は隠れたり最小化されたりしたウィンドウのプロセスに高い分解能を保証しない - [CreateWaitableTimerExW function (synchapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createwaitabletimerexw) · Microsoft · 高分解能の待機タイマーのフラグCREATE_WAITABLE_TIMER_HIGH_RESOLUTION - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsBetweenPresents:今回のPresent()呼び出しと前回の呼び出しの間の時間(ms) - [Results for the Idle Energy Efficiency Assessment](https://learn.microsoft.com/en-us/windows-hardware/test/assessments/results-for-the-idle-energy-efficiency-assessment) · Microsoft · デフォルトのシステムタイマー分解能は15.6ms。エネルギーレポートの「Platform Timer Resolution」項目で、タイマー分解能を変更したプロセスを見つける - [Powercfg command-line options](https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/powercfg-command-line-options) · Microsoft · powercfg /energy:システムを分析してエネルギーレポート(HTML)を作成 #### co-mobile-bg · モバイルアプリのバックグラウンド移行 · App suspended in background 通知を見ようとアプリをいったんバックグラウンドに移すと、OSが数秒後にアプリを一時停止(suspend)し、その間にサーバーは自分を切断します。 - なぜ → すると → 画面では:メッセージの確認・電話でゲームをバックグラウンドに移す → ゲームエンジンがゲームの進行を止め、OSもすぐにアプリとネットワークを停止させる → 戻るとすでに切断されていて、再接続 - 症状:切断 / 要因:ストール, パケットロス - 誰に:自分だけ / いつ:しばらく放置した後, 特定の操作をしたとき - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:戻ったら切れた接続を待たずに、セッショントークンですぐに自動再接続(再ログインなしで接続を引き継ぐ)、最新の状態を一度に受け取って合わせる。サーバー:ハートビートが途切れたら接続は片付けるが、キャラクターのセッションは短い猶予時間のあいだ維持(すぐに追い出さない)、その間に再接続すればセッショントークンで引き継ぐ。 - 数値の目安:ゲームエンジンは通常、バックグラウンドに移した瞬間に止まります。iOSは数秒、追加の時間をもらってもたいてい数十秒以内にアプリを一時停止し、Android 14以降は画面から外れたアプリを10秒ほどで凍結(freeze)します。 - グラフでは:接続が一斉に切れる(切断数(ハートビートのタイムアウト)、アプリの一時停止の記録) - 確認箇所:クライアントログにあるアプリの一時停止・復帰の時刻(UnityではOnApplicationPause)と、サーバーの切断理由・時刻を、セッションIDで突き合わせる。Androidは、ApplicationExitInfoに残ったプロセスの終了理由(REASON_LOW_MEMORYなど)もあわせて確認 - 該当する場合:サーバーのハートビートタイムアウトによる切断の直前にクライアントが一時停止に入り、復帰直後に再接続している - 該当しない場合:アプリをフォアグラウンドにしている間に切れたなら「NATマッピングの期限切れ」「通信事業者の共有IP(CGNAT)」「Wi-Fi ↔ LTE・5Gの切り替え」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:スマホはメモリが足りないと、バックグラウンドのゲームを完全に終了させることもあります。カメラや決済・認証アプリから戻るとゲームが最初から起動し直すのはこのためです。低スペックの端末ほどよく起きます。 - 出典: - [Extending your app’s background execution time](https://developer.apple.com/documentation/uikit/extending-your-app-s-background-execution-time) · Apple · バックグラウンドに移るとapplicationDidEnterBackgroundに5秒が与えられ、すぐに一時停止。さらに必要ならbeginBackgroundTaskで時間を要求(残り時間はbackgroundTimeRemaining) - [Cached apps freezer](https://source.android.com/docs/core/perf/cached-apps-freezer) · Android (Google) · Android 14以降は、キャッシュ状態になったアプリのプロセスを10秒後に凍結し、凍結されるとすべてのスレッドが止まる - [Application.runInBackground](https://docs.unity3d.com/ScriptReference/Application-runInBackground.html) · Unity · デフォルト値のfalseではバックグラウンドで止まる。Androidはバックグラウンドなら設定にかかわらず止まり、iOSはこの設定を無視 - [MonoBehaviour.OnApplicationPause(bool)](https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnApplicationPause.html) · Unity · アプリが一時停止されたり再開されたりするとき、すべてのMonoBehaviourにOnApplicationPause(true/false)を送る - [ApplicationExitInfo](https://developer.android.com/reference/android/app/ApplicationExitInfo) · Android (Google) · REASON_LOW_MEMORY:システムの低メモリキラーがアプリのプロセスを終了させた(非対応の端末ではREASON_SIGNALED・SIGKILLとして報告) #### co-netswitch · Wi-Fi ↔ LTE・5Gの切り替え · Network switch changes IP 家の外に出てWi-Fiが切れ、LTE・5Gに切り替わると、自分のIPアドレスが変わり、それまでの接続が無効になります。 - なぜ → すると → 画面では:Wi-Fiの電波が弱くなり、モバイル回線に切り替わる → 自分のIPアドレスが変わり、古いアドレスで確立した接続ではもうやり取りできない → 少し止まった後に切断、または再接続 - 症状:フリーズ, 切断 / 要因:パケットロス - 誰に:自分だけ / いつ:移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発, インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:サーバー:アドレスが変わってもセッショントークンで同じプレイヤーとして引き継ぎ、古いアドレスの接続はすぐに片付ける。アドレスが変わっても接続が続くプロトコル(QUICのコネクションマイグレーションなど)を検討。クライアント:ネットワークの切り替えを検知したら、ハートビートのタイムアウトを待たずにセッショントークンですぐ再接続。 - インフラチームの対応:QUICのコネクションマイグレーションを使う場合は、ロードバランサーがアドレス・ポートの代わりにコネクションIDでサーバーを選ぶよう設定(アドレス・ポートで選ぶと、アドレスが変わったパケットが別のサーバーへ行ってしまう)。 - グラフでは:接続が一斉に切れる(切断・再接続の数、再接続時に変わったIP) - 確認箇所:サーバーの接続ログで、同じセッショントークンが別のIPから再接続した記録を探し、クライアントのデフォルトネットワーク変更コールバック(registerDefaultNetworkCallback)の時刻と突き合わせる - 該当する場合:切断直後に再接続したIPが、Wi-Fi(自宅回線)のアドレス帯からモバイルキャリアのアドレス帯へ、またはその逆へ変わっていて、その直前にネットワーク切り替えのコールバックが来ている - 該当しない場合:IPが変わらないのに切れたなら「基地局のハンドオーバー(移動中)」か「モバイル電波の弱さ・不感地帯」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Read network state](https://developer.android.com/training/basics/network-ops/reading-network-state) · Android (Google) · デフォルトネットワークが変わると、新しい接続は新しいネットワークを使い、以前のネットワークの接続は最終的に強制的に切られる。registerDefaultNetworkCallbackで切り替えを検知 - [RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport](https://www.rfc-editor.org/rfc/rfc9000) · IETF · コネクションIDにより、IPアドレス・ポートが変わっても接続を維持(9章)。アドレス・ポートだけで振り分けるロードバランサーは、アドレスが変わったパケットを別のサーバーに送ってしまう可能性がある(5.2.3節) - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · TCP接続は、両端のソケット(アドレス・ポート)のペアで識別される #### co-security · セキュリティソフトによるパケット検査 · Antivirus / firewall inspection ウイルス対策ソフト・ファイアウォールがすべてのパケットを検査すると遅延が増え、行き過ぎるとゲームを攻撃と誤認してブロックします。 - なぜ → すると → 画面では:セキュリティソフトが送受信パケットを1つずつ検査 → パケットごとに遅延が加わり、検査が追いつかないと破棄される → Pingが不規則に跳ねるか、接続がブロックされる - 症状:カクつき, 接続不可・無限ロード / 要因:ジッター, パケットロス - 誰に:自分だけ / いつ:常に, 接続直後・メンテ明け - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:セキュリティソフトの互換性リストの管理、インストール時にWindowsファイアウォールへゲームの例外を登録。 - 外部の対応:ユーザーに、セキュリティソフトでゲームを除外に登録するよう案内。ゲームを攻撃と誤認する場合は、セキュリティソフトのベンダーに誤検知の修正を依頼。 - 数値の目安:正常時のパケット検査にかかる時間は、通常1msにも満たない程度です。問題になるのは、検査モジュールの処理が追いつかないときやバグがあるとき、ゲームの通信を攻撃と誤認したときです。 - グラフでは:一部だけ高い(RTT・接続失敗(ユーザー別)) - 確認箇所:セキュリティソフトを一時的に無効にするか、ゲームを除外に登録してから比較。Windowsでは、監査ポリシーのAudit Filtering Platform Connection・Audit Filtering Platform Packet Dropを有効にするとセキュリティログに5157(接続のブロック)・5152(パケットのブロック)が記録され、パフォーマンスモニターのWFPv4\Packets Discarded/secで破棄されたパケット数を確認できる - 該当する場合:ゲームサーバーのアドレスへの接続・パケットのブロック記録が残るか、セキュリティソフトを無効にするとPingのスパイク・接続不可が消える - 該当しない場合:セキュリティソフトと関係なく、同じ家のほかの端末でも同じならルーター・回線側 - 確認手段:ユーザー側の環境で確認 - 出典: - [About Windows Filtering Platform](https://learn.microsoft.com/en-us/windows/win32/fwp/about-windows-filtering-platform) · Microsoft · Windowsのネットワークスタックのフックとフィルターエンジンでパケットを許可・ブロックする仕組み。外部のセキュリティベンダーが独自のフィルターモジュール(コールアウト)を組み込める - [Windows Firewall Rules](https://learn.microsoft.com/en-us/windows/security/operating-system-security/network-security/windows-firewall/rules) · Microsoft · デフォルトでは受信接続がブロックされるため、アプリには例外ルールが必要。多くの場合、アプリのインストーラーがルールを作成する - [Address false positives/negatives in Microsoft Defender for Endpoint](https://learn.microsoft.com/en-us/defender-endpoint/defender-endpoint-false-positives-negatives) · Microsoft · 正常なプログラムを脅威と誤認(誤検知)したときの除外設定と、Microsoftに分析を依頼する手順 - [5157(F): The Windows Filtering Platform has blocked a connection.](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-5157) · Microsoft · イベント5157:Windows Filtering Platformが接続をブロックした(Audit Filtering Platform Connection) - [Audit Filtering Platform Packet Drop](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/audit-filtering-platform-packet-drop) · Microsoft · イベント5152:Windows Filtering Platformがパケットをブロックした - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · WFPv4・WFPv6:Packets Discarded/secカウンター #### co-rcvbuf · 受信バッファのあふれ · Socket receive buffer overflow ゲームの処理が忙しく、ソケット(OSが提供するネットワーク送受信のインターフェース)からパケットを取り出すのが遅れると、OSのバッファがあふれます。 - なぜ → すると → 画面では:フレームが遅れ、ゲームがソケットを読むのが遅くなる → OSの受信バッファがいっぱいになり、UDPは破棄され、TCPは受信ウィンドウを縮めて送信側を止めさせる → ワープ(UDP)または早送り(TCP) - 症状:ワープ, 早送り / 要因:パケットロス, ストール - 誰に:自分だけ / いつ:人が集中したとき - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:受信専用スレッド、バッファサイズの調整(SO_RCVBUF)。 - 数値の目安:デフォルトの受信バッファは、OS・設定によって数十〜数百KB。人が多い場所の更新は、1秒に数百KBになることもあります。 - グラフでは:人数・負荷に連動して上昇(UDP受信バッファでの破棄数、フレームタイム) - 確認箇所:Windowsのパフォーマンスモニターで、Microsoft Winsock BSP\Dropped Datagrams(ソケットの受信バッファ不足で破棄されたUDPの数)とUDPv4\Datagrams Received Errorsをフレームタイムとあわせて記録し、ゲーム側では受信パケットの連番の抜けを数える - 該当する場合:人が多い場所や長いフレームの直後にDropped Datagramsが増え、同じ瞬間にゲームの連番に抜けが出る。同じ時刻に回線側のパケットロスはない - 該当しない場合:Dropped Datagramsは変わらないのに連番だけ抜けるなら、経路上のパケットロス - 確認手段:ユーザー側の環境で確認 - 出典: - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · SO_RCVBUFはソケットの受信バッファの最大サイズ。デフォルト値はrmem_default、最大値はrmem_maxで決まる(AndroidもLinuxカーネル) - [SOL_SOCKET Socket Options (Winsock2.h)](https://learn.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options) · Microsoft · WindowsのSO_RCVBUF:ソケットごとに受信用に確保するバッファ領域 - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · TCPのウィンドウフィールドは、受信側がさらに受け取れるバイト数。0なら送信側はゼロウィンドウプローブだけを送って待つ - [Low Latency Workloads Management and Operations](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh997022(v=ws.11)) · Microsoft · Microsoft Winsock BSPカウンターセットのDropped Datagrams・Dropped Datagrams/sec:アプリの処理速度よりUDPが速く届いたり、受信ソケットのバッファが足りなかったりして破棄された数 - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · UDPv4・UDPv6:Datagrams Received Errors、Microsoft Winsock BSP:Dropped Datagramsカウンター #### co-swap · クライアントのメモリ不足・スワップ · Paging / swap on client ブラウザのタブを数十個開いたままゲームを動かすと、OSがゲームのメモリの一部をディスクに追い出します。 - なぜ → すると → 画面では:全体のRAMが足りなくなる → OSが今すぐには使わないゲームのメモリをディスクに移す → その部分を再び使う瞬間に、ストレージによって数十〜数百ms止まる - 症状:フリーズ, カクつき / 要因:ストール - 誰に:自分だけ / いつ:移動中・マップ切り替え時, ときどきランダムに - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:メモリ使用量の削減、空きメモリが少なければ警告を表示。 - 外部の対応:ユーザーに最低スペックを案内、プレイ中は他のプログラム(ブラウザのタブなど)を閉じるよう案内。 - グラフでは:不定期なスパイク(ハードページフォールト、メモリ使用量) - 確認箇所:パフォーマンスモニターのMemory\Pages Input/sec(ハードページフォールトを解決するためにディスクから読み込んだページ数)と、タスクマネージャーのパフォーマンスタブのメモリ使用量・コミット済みを、フレームタイムとあわせて記録 - 該当する場合:止まった瞬間にPages Input/secが急上昇し、メモリがほぼいっぱい。ブラウザなど他のプログラムを閉じると消える - 該当しない場合:メモリに余裕があり、Pages Input/secが落ち着いているなら「メインスレッドでの同期ロード・シェーダーコンパイル」か「ストレージが遅くアセットストリーミングが間に合わない」 - 確認手段:ユーザー側の環境で確認 - 出典: - [Introduction to the page file](https://learn.microsoft.com/en-us/troubleshoot/windows-client/performance/introduction-to-the-page-file) · Microsoft · ページファイルはディスク上のファイルで、あまり使われない変更済みのメモリページをRAMから追い出すために使う - [Working Set](https://learn.microsoft.com/en-us/windows/win32/memory/working-set) · Microsoft · RAMにないページにアクセスするとページフォールト。ハードフォールトは、ページファイルなどディスクから読み込まないと解決しない - [Chapter 12 - Detecting Memory Bottlenecks](https://learn.microsoft.com/en-us/previous-versions/cc749872(v=technet.10)) · Microsoft · Memory\Pages Input/sec:ページフォールトを解決するためにディスクから読み込んだページ数(ハードページフォールト) #### co-vram · ビデオメモリ(VRAM)不足 · VRAM over-commit グラフィックオプションが必要とするメモリがグラフィックボードのメモリより大きいと、OSがテクスチャをPCのメモリへ追い出してはまた戻すため、カクつきます。 - なぜ → すると → 画面では:高いテクスチャ設定や、人が多い場所のさまざまな装備・エフェクトで、グラフィックボードのメモリがいっぱいになる → OSが今すぐには使わないテクスチャをPCのメモリに移し、必要になると遅いPCIeバスで再び戻す → 新しい場面や新しいキャラクターが見えるたびに一瞬止まり、テクスチャがしばらくぼやける - 症状:カクつき, フリーズ / 要因:ストール - 誰に:自分だけ / いつ:人が集中したとき, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:外部・外部 - ゲーム開発チームの対応:グラフィックボードのメモリ容量に合わせたオプションのデフォルト値、メモリ予算を超えたらテクスチャ品質を自動で下げる、人が多い場所ではキャラクターのテクスチャを簡略化。 - 外部の対応:ユーザーにテクスチャ設定を下げるよう案内。クライアントを2つ起動する場合は、さらに設定を下げるよう案内。 - 数値の目安:グラフィックボードのメモリは1秒に数百GBを読み込めますが、PCのメモリとやり取りするPCIeバスは世代によって1秒に16〜64GB前後で、10倍以上遅くなります。 - グラフでは:上限で頭打ち(専用GPUメモリ、共有GPUメモリ) - 確認箇所:タスクマネージャーのパフォーマンスタブにあるGPU項目の、専用GPUメモリ・共有GPUメモリのグラフ(詳細タブにプロセスごとの列も追加可能)を、PresentMonのフレームタイムとあわせて確認 - 該当する場合:専用GPUメモリが上限に張り付いて頭打ちになり、共有GPUメモリが増えている間に一瞬止まることが多い。テクスチャ設定を下げると消える - 該当しない場合:専用メモリに余裕があれば「ストレージが遅くアセットストリーミングが間に合わない」か「メインスレッドでの同期ロード・シェーダーコンパイル」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:Windowsのタスクマネージャーで、GPU項目の「専用GPUメモリ」がいっぱいになり「共有GPUメモリ」が増えていれば、この状態です。同じPCでクライアントを2つ起動すると、さらに早くいっぱいになります(「メモリ・VRAM不足によるストリーミングの失敗」の項目)。 - 出典: - [Residency](https://learn.microsoft.com/en-us/windows/win32/direct3d12/residency) · Microsoft · プロセスが使えるビデオメモリには予算があり、超えるとカーネルがディスクリートGPUのヒープの一部をPCのメモリに移す(最後の手段なので、予算の管理を推奨) - [GPUs in the task manager](https://devblogs.microsoft.com/directx/gpus-in-the-task-manager/) · Microsoft · タスクマネージャーの専用GPUメモリはグラフィックボードのVRAM、共有GPUメモリはGPUとCPUが共有して使うPCのメモリ - [CUDA C++ Best Practices Guide](https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html) · NVIDIA · ビデオメモリの帯域幅(V100で898GB/s)はPCIe Gen3 x16(16GB/s)よりはるかに大きいため、PCのメモリとの転送を減らすよう推奨 - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime(フレーム間のCPU時間)でフレームごとの時間を記録 #### co-wifi-scan · Wi-Fiのバックグラウンドスキャン · Periodic Wi-Fi background scan OSが周囲のWi-Fiを探すために定期的にチャンネルを切り替えている間、通信が一瞬止まります。 - なぜ → すると → 画面では:OS・ドライバーが一定周期で周囲のWi-Fiを検索 → 検索している間、送受信が一瞬止まる → 正確に一定の間隔(例:60秒ごと)でPingが跳ねる - 症状:カクつき, ワープ / 要因:ジッター - 誰に:自分だけ / いつ:一定の周期で - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:プレイ中は無線の検索を減らすモードを要求(AndroidはWi-Fi低遅延モードWIFI_MODE_FULL_LOW_LATENCY、WindowsはWlanSetInterfaceのメディアストリーミングモード。端末・ドライバーによっては効果がないこともある)。 - 外部の対応:ユーザーに、有線接続、位置情報サービス・Wi-Fiの自動検索の設定調整、無線LANドライバーの更新を案内。 - 数値の目安:通常は1回あたり数十〜数百ms。規則的すぎるなら、まずこの原因を疑います。 - グラフでは:周期的なスパイク(ルーターまでのRTT) - 確認箇所:プレイ中にping /tでルーターのアドレス(ipconfigのデフォルトゲートウェイ)を数分間測り、跳ねる間隔を測定。有線に切り替えて同じ測定 - 該当する場合:ルーターまでのPingが正確に一定の間隔(例:60秒)で数十〜数百ms跳ね、有線では消える - 該当しない場合:跳ねる間隔が不規則なら「Wi-Fiの干渉・電波の弱さ」。ルーターまでは問題なく、その先だけ跳ねるなら回線・通信事業者の区間 - 確認手段:ユーザー側の環境で確認 - 出典: - [WDI low latency connection quality](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/wdi-low-latency-connection-quality) · Microsoft · 検索・ローミングは無線チップを接続中のチャンネルの外へ移すため、低遅延モードではチャンネルを離れる時間と検索を制限 - [WlanSetInterface function (wlanapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlansetinterface) · Microsoft · Windowsで、バックグラウンドスキャン(wlan_intf_opcode_background_scan_enabled)とメディアストリーミングモードをオン・オフするAPI - [Wi-Fi low-latency mode](https://source.android.com/docs/core/connect/wifi-low-latency) · Android (Google) · 低遅延モードではWi-Fiの省電力を無効にする。検索・ローミング設定の最適化は端末メーカーの実装による - [ping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) · Microsoft · /t:停止するまでエコー要求を送り続ける - [ipconfig](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ipconfig) · Microsoft · パラメータなしで実行すると、アダプターごとのIPv4・IPv6アドレスとデフォルトゲートウェイを表示 #### co-driver · NICの省電力・ドライバーの問題 · NIC power saving, driver bugs 有線LANアダプター・Wi-Fiチップがパケットの合間に省電力状態に入ると、再び動き出すまでに時間がかかります。 - なぜ → すると → 画面では:ネットワークデバイスの省電力機能が有効か、ドライバーが古い → 省電力状態からの復帰(wake-up)の遅延、ときどきデバイスの再起動 → 不規則な遅延、まれに数秒止まる - 症状:カクつき, フリーズ / 要因:ジッター, パケットロス - 誰に:自分だけ / いつ:しばらく放置した後, ときどきランダムに - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:Androidクライアントは、プレイ中に低遅延Wi-Fiモード(WIFI_MODE_FULL_LOW_LATENCY)を要求してWi-Fiの省電力をオフにする。 - 外部の対応:ユーザーに、ネットワークドライバーの更新、デバイスマネージャーでネットワークデバイスの省電力を解除することを案内。画面全体が一瞬止まり音にノイズが入るなら、LatencyMonで原因のドライバーを見つけるよう案内。 - グラフでは:不定期なスパイク(DPC・ISRの時間、ルーターまでのRTT) - 確認箇所:Windows Performance Recorder(WPR)で記録し、Windows Performance Analyzer(WPA)のDPC/ISRグラフで長く実行されているドライバー(Module列)を探す。デバイスマネージャーで、ネットワークアダプターの電源の管理(省電力)設定を確認 - 該当する場合:一瞬止まった時刻にネットワークドライバーのDPC・ISRが数msずつ続くか、省電力をオフにすると不規則な遅延が消える - 該当しない場合:DPCが短く、省電力をオフにしても同じなら「Wi-Fiの干渉・電波の弱さ」か「Wi-Fiのバックグラウンドスキャン」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:ドライバーが割り込みの処理でCPUを長く占有すると(WindowsではDPC遅延と呼びます)、その間ゲームスレッドもコアを使えません。このときはCPU使用率が低いのに画面全体が一瞬止まり、音にもノイズが入ります。LatencyMonのようなツールでどのドライバーかを特定でき、Wi-Fi・有線LANのドライバーがよくある原因です。 - 出典: - [Introduction to NDIS Selective Suspend](https://learn.microsoft.com/en-us/windows-hardware/drivers/network/ndis-selective-suspend) · Microsoft · Windowsは、アイドル状態のネットワークアダプターを低電力状態にできる(セレクティブサスペンド) - [Guidelines for Writing DPC Routines](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/guidelines-for-writing-dpc-routines) · Microsoft · DPCの実行中はそのコアのすべてのスレッドが止まるため、1回あたり100µsを超えないよう推奨 - [Wi-Fi low-latency mode](https://source.android.com/docs/core/connect/wifi-low-latency) · Android (Google) · Androidの低遅延Wi-Fiモードでは、フレームワークがWi-Fiの省電力を明示的にオフにする - [CPU Analysis](https://learn.microsoft.com/en-us/windows-hardware/test/wpt/cpu-analysis) · Microsoft · WPAのDPC/ISRグラフ:DPC・ISRが途切れずに実行された区間ごとの時間と、その関数を含むモジュール(Module) #### co-other-apps · 同じ端末の他のアプリによる帯域幅の占有 · Other apps saturating the link クラウド同期、大容量のダウンロード、ゲームのアップデートが同じPCで動いていると、ゲームのパケットがキューで待たされます。 - なぜ → すると → 画面では:他のアプリが上り・下りを目いっぱい使う → PCとルーターのキューにゲームのパケットがたまる → Pingの急上昇、入力遅延、早送り - 症状:入力遅延, 早送り / 要因:遅延, ジッター - 誰に:自分だけ, 同じ家 / いつ:ときどきランダムに - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:自社のランチャー・パッチャーは、プレイ中にバックグラウンドのダウンロードを止めるか速度を制限。 - 外部の対応:ユーザーに、ダウンロード速度の制限、プレイ中は自動更新をオフにすることを案内。 - グラフでは:人数・負荷に連動して上昇(RTT、PCの送受信量) - 確認箇所:パフォーマンスモニターのNetwork Interface\Bytes Sent/sec・Bytes Received/secとPingをあわせて記録。Pingを測りながらわざと大容量の転送をかける、バッファブロートテストと同じ方法 - 該当する場合:ダウンロード・アップロードが回線速度近くまで埋まっている間、Pingが数十〜数百ms上がり、転送を止めるとすぐ戻る - 該当しない場合:PCの送受信量が少ないのにPingが上がるなら、同じ家のほかの端末による「バッファブロート(ルーターのキュー)」か、通信事業者の区間 - 確認手段:ユーザー側の環境で確認 - 出典: - [Introduction](https://www.bufferbloat.net/projects/bloat/wiki/Introduction/) · Bufferbloat.net · ルーターなどのネットワーク機器がデータをためすぎると、遅延が大きく跳ねる(バッファブロート) - [Delivery Optimization reference](https://learn.microsoft.com/en-us/windows/deployment/do/waas-delivery-optimization-reference) · Microsoft · Windows Updateのダウンロード(配信の最適化)はデフォルトで利用可能な帯域幅に合わせて動的に調整され、バックグラウンド・フォアグラウンドのダウンロード帯域幅の上限を設定できる - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · Network Interface:Bytes Received/sec・Bytes Sent/secカウンター - [Tests for Bufferbloat](https://www.bufferbloat.net/projects/bloat/wiki/Tests_for_Bufferbloat/) · Bufferbloat.net · Pingを測りながら速度テストで回線を埋め、Pingが上がればバッファブロート #### co-unfocused · ウィンドウの最小化・非アクティブ時の処理制限 · Minimized / unfocused window throttling ほかのウィンドウを見たりゲームを最小化したりすると、ゲームとWindowsが電力を節約するためにゲームの処理を遅くします。戻ると遅れていたパケットが押し寄せるか、すでに切断されています。 - なぜ → すると → 画面では:Alt+Tabでほかのウィンドウを見るか、ゲームを最小化 → ゲームが見えない間はFPSを大きく下げるか止め、Windowsも見えないプログラムの優先度を下げる → 戻った瞬間に早送り、長くバックグラウンドにしていたら切断 - 症状:早送り, カクつき, 切断 / 要因:ストール - 誰に:自分だけ / いつ:特定の操作をしたとき, しばらく放置した後 - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:ウィンドウが見えなくてもパケットの受信とハートビートは別スレッドで継続、エンジンの「バックグラウンドで実行」設定を確認、復帰時に最新の状態へ一度に合わせる。 - 数値の目安:ウィンドウが見えないときにFPSを5〜10に下げると、1フレームが100〜200ms。パケットをフレームごとに処理するゲームは、その分パケットを読むのが遅れます。 - グラフでは:途切れた後にまとめて到着(フレーム間隔(ウィンドウ切り替えの前後)、処理したパケット数) - 確認箇所:PresentMonを動かしたままAlt+Tab・最小化を行い、ウィンドウが見えないときのフレーム間隔を確認。ゲームのログにウィンドウのフォーカスが変わった時刻を記録し、切断理由と突き合わせる - 該当する場合:ウィンドウが見えない間にフレーム間隔が100ms以上に延びるか記録が途切れ、戻った瞬間に遅れていたパケットを一気に処理して早送りになる。長くバックグラウンドにしておくと、ハートビートのタイムアウトで切断 - 該当しない場合:ウィンドウを表示したままでも同じなら「バックグラウンドプロセスによるCPU占有」かネットワーク側 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:Windows 11は、最小化されたり完全に隠れていて音も出していないウィンドウのプログラムに、1msタイマーを保証しません。バッテリーで動いているノートPCでは、そうしたプログラムを最も電力消費の少ない速度に落とし、種類の異なるコアが混在するCPUなら、遅い高効率コアで動かすこともあります。同じPCの2つのクライアントのうちバックグラウンド側だけがおかしいなら、「バックグラウンドウィンドウの処理制限」の項目もあわせて確認してください。 - 出典: - [Quality of Service](https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service) · Microsoft · 見えも聞こえもしないウィンドウのプログラムはLow QoS。バッテリー駆動時は、最も効率的なCPU速度と高効率コアでスケジューリング - [timeBeginPeriod function (timeapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod) · Microsoft · Windows 11は、隠れたり最小化されたりしたウィンドウのプロセスに、デフォルトより高いタイマー分解能を保証しない - [Application.runInBackground](https://docs.unity3d.com/ScriptReference/Application-runInBackground.html) · Unity · Unityのデフォルト値はfalseのため、ウィンドウがバックグラウンドに回るとゲームループが止まる - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsBetweenPresents:今回のPresent()呼び出しと前回の呼び出しの間の時間(ms) #### co-overlay · オーバーレイソフトの干渉 · Overlays and screen hooks メッセンジャー・ランチャー・録画・FPS表示のソフトが、ゲーム画面の上に自分のUIを重ねて描くため、ゲームのレンダリング処理に割り込みます(フック)。フレームごとの処理が増え、ときどきゲームとぶつかって一瞬止まったり、ゲームが強制終了したりします。 - なぜ → すると → 画面では:メッセンジャー・ゲームランチャー・グラフィックボードのツール・録画ソフトのオーバーレイが有効 → フレームを画面に出力するたびにオーバーレイが割り込み、自分のUIを重ねて描く → フレームが少しずつ遅れ、通知が出た瞬間に一瞬止まったり、グラフィックの不具合・強制終了が起きたりする(プレイヤーには切断のように見える) - 症状:カクつき, フリーズ, 切断 / 要因:ストール - 誰に:自分だけ / いつ:常に, ときどきランダムに - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:クラッシュレポートとカクつきのログに、実行中のオーバーレイの一覧もあわせて収集。 - 外部の対応:報告があれば、ユーザーにオーバーレイをすべてオフにして再度試すよう案内。 - グラフでは:一部だけ高い(フレームタイム・クラッシュ数(オーバーレイを有効にしているユーザー)) - 確認箇所:オーバーレイをすべてオフにして同じ場面のPresentMonのフレームタイムを比較し、クラッシュがあればイベントビューアーのイベントID 1000にある、障害が発生しているモジュールの名前(Faulting module name)を確認 - 該当する場合:オーバーレイをオフにすると一瞬の停止・グラフィックの不具合が消えるか、クラッシュの障害モジュールがオーバーレイソフトのDLL - 該当しない場合:オーバーレイをすべてオフにしても同じなら、グラフィックドライバーか「クライアントのクラッシュ」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:特定の人だけがカクついたりゲームが落ちたりして、スペックでは説明がつかないときは、まずオーバーレイとゲームのセキュリティモジュールの衝突を疑います。 - 出典: - [Steam Overlay (Steamworks Documentation)](https://partner.steamgames.com/doc/features/overlay) · Valve · Steamオーバーレイは、Steamから起動したゲームに自動でフックして割り込む。その仕組みのせいで、ゲームのレンダリングAPIの使い方にあるメモリエラーが表面化し、クラッシュすることもある - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · FrameTime(フレーム間のCPU時間)でフレームごとの時間を記録 - [The application or service crashing behavior troubleshooting guidance](https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/troubleshoot-application-service-crashing-behavior) · Microsoft · アプリケーションログのイベントID 1000には、障害が発生しているモジュールの名前(Faulting module name)が含まれる。別のモジュールの破損が原因で、Windowsのモジュールが障害モジュールとして記録されることもある #### co-display-input · ディスプレイ・入力デバイス・フレーム生成による遅延 · Display, input device and frame generation latency Pingは正常なのに操作が重いなら、テレビの映像処理やワイヤレスコントローラー、フレーム生成機能が、入力と画面の間に遅延を上乗せしているのかもしれません。 - なぜ → すると → 画面では:テレビのゲームモードがオフ、Bluetooth・ワイヤレスコントローラーを使用、フレーム生成(DLSS・FSRのフレーム生成)が有効、のいずれか → テレビは画質処理をしている間フレームを遅れて出力し、ワイヤレス入力は送信周期と干渉の分だけ遅れて届き、フレーム生成は次のフレームを待ってから中間フレームを作る → PingとFPSの数字はよいのに、押してから画面に反映されるまでが遅く、入力遅延になる - 症状:入力遅延 / 要因:遅延 - 誰に:自分だけ / いつ:常に - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:フレーム生成は選択式のオプションにして、有効にすると入力遅延が増えることがあると案内、フレーム生成を使うときはGPUメーカーの低遅延機能(NVIDIA Reflex、AMD Anti-Lag 2)もあわせて組み込む、ゲーム内でPC側の入力〜画面の遅延を表示、Android TV・セットトップボックス向けビルドではWindow.setPreferMinimalPostProcessing(true)でテレビに低遅延モード(ALLM)を要求。 - 外部の対応:ユーザーに、テレビ・モニターのゲームモード(ALLM)を有効にすること、競技性の高いコンテンツでは有線コントローラーを使いフレーム生成をオフにすること、Bluetooth機器は近くに置きWi-Fiは5GHzを使うことを案内。 - 数値の目安:60Hzの画面は1フレームを送るだけで16.7ms、120Hzなら8.3msかかります。以前のXboxコントローラーは8msごとに入力を読み取って送っていました。テレビの映像処理が加える遅延は機種ごとに異なり一つの数字では言えませんが、ゲームモードはこの処理を減らす設定です。フレーム生成は、生成前のフレームレートが60FPS以上の状態で使うようAMDが推奨しています。 - グラフでは:最初から常に高い(入力〜画面の遅延) - 確認箇所:PresentMonのMsAllInputToPhotonLatency(キーボード・マウスの入力から画面への出力まで)を、フレーム生成のオン・オフで比較し、FrameType(ドライバー・SDKが通知する場合にだけ記録)で、生成された中間フレームが混じっているかを確認。この値にはコントローラーの無線区間とテレビ内部の処理が含まれないため、その部分はテレビのゲームモード・有線コントローラーに切り替えながら比較 - 該当する場合:Pingは正常で、フレーム生成をオフにすると入力〜画面の遅延が減るか、テレビのゲームモード・有線コントローラーに切り替えると体感の遅延が消える - 該当しない場合:これらの設定をすべて変えても同じで、Pingが高いか跳ねるならネットワーク側。PC側の遅延がV-Sync・フレームキューのせいで高いなら「V-Syncとレンダーキュー」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:ネットワークの遅延はPingで見えますが、この遅延はPingには表れません。そのため「Pingは低いのにラグい」という報告で、まず確認すべきところです。フレーム生成は画面のFPSの数字を2倍ほどに上げますが、中間フレームを作るには次の本物のフレームを待つ必要があるため、入力が画面に反映されるまでの時間は延びます(AMDは設計上遅延が増えると明言)。Bluetooth機器はWi-Fiと同じ2.4GHz帯を使うため、干渉を受けると入力が途切れたり飛んだりすることがあります。PC内の遅延を大きくするV-Sync・レンダーキューについては、「V-Syncとレンダーキュー」の項目を見てください。 - 出典: - [Auto Low Latency Mode (ALLM)](https://www.hdmi.org/spec21sub/autolowlatencymode) · HDMI Licensing Administrator · ALLMは、機器がディスプレイを低遅延モード(一般にゲームモード)へ自動で切り替えられるようにする。低遅延モードでは、遅延を減らすためにテレビの映像処理の一部を止める - [Xbox Series X: What’s the Deal with Latency?](https://news.xbox.com/en-us/2020/03/16/xbox-series-x-latency/) · Microsoft · 入力遅延はコントローラー→コンソール→HDMI→テレビとつながる経路の合計。以前のコントローラーは8msごとに入力を読み取って送信。HDMIで1フレームを送る時間は60Hzで16.6ms・120Hzで8.3ms。ALLMでテレビのゲームモードに自動で切り替え - [AMD FSR 3 game integrations out now + more details for developers](https://gpuopen.com/news/fsr3-in-games-technical-details/) · AMD · フレーム補間は設計上遅延を増やす。補間前のフレームレートが60以上で使うよう推奨。60FPSの入力で最大120FPSを出力 - [AMD FSR Frame Generation](https://gpuopen.com/amd-fsr-framegeneration/) · AMD · フレーム生成は補間前60FPS以上を推奨(30FPS未満は避けるべき)。AMD Radeon Anti-Lag 2がCPU・GPUの処理タイミングを合わせてシステムの遅延を減らす - [NVIDIA DLSS](https://developer.nvidia.com/rtx/dlss) · NVIDIA · DLSSのフレーム生成は、NVIDIA Reflex(低遅延機能)と組み合わせて応答性を保つよう設計されている - [Resolve Wi-Fi and Bluetooth issues caused by wireless interference](https://support.apple.com/en-us/102319) · Apple · 無線の干渉は、Wi-Fi・Bluetooth機器の途切れや性能低下を引き起こす。BluetoothとWi-Fiは同じ2.4GHz帯を使う - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsAllInputToPhotonLatency(入力〜画面の遅延)、DisplayLatency(フレームの提出〜モニターへの出力)、FrameType(アプリが描画したフレームと、ドライバー・SDKが補間したフレームを区別) - [PresentMon Console Application (README-ConsoleApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-ConsoleApplication.md) · Intel · MsAllInputToPhotonLatencyはキーボード・マウス入力が基準。FrameTypeは、アプリ・ドライバーがIntel-PresentMonイベントを出力しないと記録されない(--track_frame_type) - [Window.setPreferMinimalPostProcessing](https://developer.android.com/reference/android/view/Window#setPreferMinimalPostProcessing(boolean)) · Android (Google) · ゲームのように遅延が重要なウィンドウは、ディスプレイに最小限の映像処理を要求する。HDMI接続ならALLM・Game Content Typeの信号を送り、テレビを低遅延モードに切り替える ### L3 家庭内ネットワーク(原因10件) #### hn-wifi · Wi-Fiの干渉・電波の弱さ · Wi-Fi interference, weak signal 電波が弱かったり干渉があったりすると、無線区間で何度も送り直すことになり、パケットの到着がばらつきます。 - なぜ → すると → 画面では:壁・距離・電子レンジ・Bluetooth・近所のルーターによる電波品質の低下 → 無線区間で送信に失敗 → 何度も再送 → パケットの到着がばらつき(ジッター)、キャラクターが何度も一瞬止まる。ひどいとパケットロスでワープ - 症状:カクつき, ワープ, 引き戻し / 要因:ジッター, パケットロス - 誰に:自分だけ, 同じ家 / いつ:ときどきランダムに, 常に - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:回線状態に応じて補間バッファの長さを自動調整、ジッター・パケットロスが大きければ画面にネットワーク状態を表示。 - 外部の対応:ユーザーに、有線接続、5GHz・6GHzの使用、ルーターの設置場所の変更を案内。 - 数値の目安:再送1回ごとに1〜4msほど余計にかかります。電波が弱いと遅い速度で何度も送り直し、チャンネルが空くまで待つこともあるため、50〜200msずつ跳ねることもあります。平均Pingは正常に見えるのが落とし穴です。 - グラフでは:不定期なスパイク(ルーターまでのRTT) - 確認箇所:ping /tでルーターのアドレス(ipconfigのデフォルトゲートウェイ)を数分間測り、netsh wlan show networks mode=bssidで自分のルーターの電波強度・チャンネルを確認。同じ場所で有線に切り替えて比較 - 該当する場合:ルーターまでのPingの段階で不規則に数十〜数百ms跳ね、ときどきパケットロスが出て、電波強度が低い。有線やルーターの近くでは消える - 該当しない場合:ルーターまでは安定していて、その先だけ跳ねるなら回線・通信事業者の区間。一定の間隔でだけ跳ねるなら「Wi-Fiのバックグラウンドスキャン」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:メッシュWi-Fiでルーター(ノード)同士が無線でつながっている場合(無線バックホール)、中継するノードは受信中に送信できず、同じチャンネルを使う前後の区間と送信の機会を分け合うため、混雑時にはスループットが落ち、遅延が増えることがあります。バックホール専用の無線帯域がある製品は影響が小さいことがあり、ノード同士を有線(イーサネット)でつなげば、この区間は無線を通らなくなります。電力線通信アダプター(PLC)もWi-Fiと同じく、媒体が空いているかを確かめてから送る方式(CSMA/CA)を使います。家電製品が出すノイズや家電製品の電源のオン・オフによって品質が絶えず変わるため、再送とジッターが生じることがあります。 - 実際の事例:ffxiv-2021 - 出典: - [Resolve Wi-Fi and Bluetooth issues caused by wireless interference](https://support.apple.com/en-us/102319) · Apple · 電子レンジ・コードレス電話などの干渉源、Wi-FiとBluetoothが同じ2.4GHz帯を使う、5GHzへの切り替えと干渉の少ないチャンネルの選択を推奨 - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · 802.11のCSMA/CA:チャンネルが空いているときだけ送信し、使用中なら空くまで待ってから、さらにランダムなバックオフの分だけ待つ - [Ending the Anomaly: Achieving Low Latency and Airtime Fairness in WiFi (USENIX ATC 2017)](https://www.usenix.org/system/files/conference/atc17/atc17-hoiland-jorgensen.pdf) · USENIX · 負荷のかかったWi-Fiのデフォルトのキューでは数百msの遅延、遅い速度で接続した(電波の弱い)端末はFQ-CoDelを使っても中央値200ms超 - [ping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) · Microsoft · /t:停止するまでエコー要求を送り続ける - [ipconfig](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ipconfig) · Microsoft · パラメータなしで実行すると、アダプターごとのIPv4・IPv6アドレスとデフォルトゲートウェイを表示 - [Wireless network connectivity issues troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/wireless-network-connectivity-issues-troubleshooting) · Microsoft · netsh wlan show networks mode=bssid:見えるWi-FiごとにBSSID・電波強度・チャンネル・無線方式を表示 - [Capacity of Ad Hoc Wireless Networks (MobiCom 2001)](https://pdos.csail.mit.edu/papers/grid:mobicom01/paper.pdf) · ACM · 802.11の無線で何度も中継すると、ノードは受信中に送信できず、前後の区間が互いに干渉するため、一列に中継する経路のスループットは理論上3分の1(シミュレーションでは約7分の1)まで落ちる - [Electri-Fi Your Data: Measuring and Combining Power-Line Communications with WiFi (IMC 2015)](https://conferences.sigcomm.org/imc/2015/papers/p325.pdf) · ACM · 電力線通信(IEEE 1901・HomePlug AV)の市販機器はWi-Fiに似たCSMA/CAで送信し、短時間の不公平によってジッターが大きくなることがある。チャンネル品質は、家電製品が出すノイズや家電製品の電源のオン・オフ(数分〜数時間単位)によって変わる #### hn-channel · Wi-Fiチャンネルの混雑 · Crowded Wi-Fi channel マンションのようにルーターが数十台ある場所では、同じチャンネルを分け合って使うため、送信の機会を待つことになります。 - なぜ → すると → 画面では:数十台のルーターが同じ2.4GHzのチャンネルを使用 → 送信するには、他の機器の送信が終わってチャンネルが空くまで待機 → 人が帰宅する夜の時間帯にジッター(到着間隔のばらつき)が増え、カクつき - 症状:カクつき, 入力遅延 / 要因:ジッター, 遅延 - 誰に:同じ家 / いつ:夜のピーク時間帯 - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:ジッターが増えたら補間バッファの長さを自動で延ばす。 - 外部の対応:ユーザーに、5GHz・6GHz、混雑の少ないチャンネル、有線接続を案内。 - グラフでは:特定の時間帯だけ高い(ルーターまでのRTT・ジッター) - 確認箇所:netsh wlan show networks mode=bssidで周囲のWi-Fiのチャンネルと電波強度を確認し、夜と昼にルーターまでのPingを測って比較 - 該当する場合:2.4GHzの同じチャンネルに周囲のルーターが多く見え、ルーターまでのジッターが夜の時間帯だけ大きくなる。5GHz・6GHzや混雑の少ないチャンネルに移ると減る - 該当しない場合:時間帯に関係なく跳ねるなら「Wi-Fiの干渉・電波の弱さ」。ルーターまでは問題なく、夜にその先だけ悪いなら「ピーク時間帯のピアリング混雑」 - 確認手段:ユーザー側の環境で確認 - 出典: - [Recommended settings for Wi-Fi routers and access points](https://support.apple.com/en-us/102766) · Apple · 同じチャンネルを使う他のルーター・機器が干渉源、2.4GHzは20MHz幅を推奨、5GHz・6GHzは干渉の心配が少ない - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · 802.11は、チャンネルが使用中なら空くまで送信を延期し、ランダムなバックオフの後に送信(CSMA/CA) - [Wireless network connectivity issues troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/wireless-network-connectivity-issues-troubleshooting) · Microsoft · netsh wlan show networks mode=bssid:見えるWi-FiごとにBSSID・電波強度・チャンネル・無線方式を表示 - [ping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) · Microsoft · /t:停止するまでエコー要求を送り続ける #### hn-bufferbloat · バッファブロート(ルーターのキュー) · Bufferbloat 家族の誰かが動画をアップロードしたり大きなファイルをダウンロードしたりすると、ルーターのキューに数百ms分のパケットがたまり、ゲームのパケットもその後ろで待たされます。 - なぜ → すると → 画面では:家族の動画アップロード・クラウドバックアップ、自分の配信、大容量ダウンロードで回線がいっぱいになる → ルーターやモデムが、あふれたパケットを大きなキューにためておく → ゲームのパケットもキューの後ろで待たされ、Pingが数百msまで急上昇 - 症状:入力遅延, 早送り, ワープ / 要因:遅延, ジッター - 誰に:同じ家, 自分だけ / いつ:ときどきランダムに, 夜のピーク時間帯 - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:Pingが急に数百msまで上がったら画面にネットワーク状態を表示(同じ回線で大容量の転送をしている可能性を案内)。 - 外部の対応:ユーザーに、SQM(fq_codel、CAKE)やQoSのあるルーターの使用、SQMの速度を回線速度の90〜95%に設定すること(こうするとキューがルーターの中にでき、効果が出る)、上りの速度制限を案内。 - 数値の目安:上り10Mbpsの回線に1MBのバッファがあると、キューは800ms分まで長くなります。 - グラフでは:人数・負荷に連動して上昇(RTT、回線の上り・下りの使用量) - 確認箇所:Pingを実行したまま速度テストで回線をいっぱいにしてみるか、負荷中の遅延を測るWebテストを使う(Bufferbloat.netの案内)。ルーターの管理画面にある上り・下りの使用量とあわせて確認 - 該当する場合:上りか下りが回線を埋めている間、Pingが数百msまで上がり、転送が終わると戻る(負荷中の遅延が50msを超えたら疑う)。SQMを有効にすると消える - 該当しない場合:回線が空いているのにPingが跳ねるなら「Wi-Fiの干渉・電波の弱さ」か「回線品質の不良」 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:特に詰まりやすいのは上り側です。ケーブル回線やモバイル回線は、上りが下りよりずっと細いことが多いためです。光回線に余裕のある家では、Wi-Fi区間がボトルネックになり、ルーターの無線キューで同じことが起きます。ゲームのパケットは小さいので帯域幅はほとんど使いませんが、キューで待たされる点は同じです。上り方向だけが詰まると、自分の入力だけが遅れ、他人の動きは正常です。スマホでは、同じスマホの写真バックアップやアプリの更新がスマホのモデムと基地局のキューを埋め、同じことが起きます。 - 出典: - [Setting up SQM for CeroWrt 3.10](https://www.bufferbloat.net/projects/cerowrt/wiki/Setting_up_SQM_for_CeroWrt_310/) · Bufferbloat.net · SQMの速度を実測速度の95%(公称速度を基準にするなら85%)に下げ、ボトルネックを通信事業者の機器からルーターの中に移さないと効果が出ない - [SQM (Smart Queue Management)](https://openwrt.org/docs/guide-user/network/traffic-shaping/sqm) · OpenWrt · 下り・上りの速度を実測値の90%で入力、キューイング方式はcakeを推奨(CPUが弱ければfq_codel) - [Ending the Anomaly: Achieving Low Latency and Airtime Fairness in WiFi (USENIX ATC 2017)](https://www.usenix.org/system/files/conference/atc17/atc17-hoiland-jorgensen.pdf) · USENIX · Wi-Fi区間がいっぱいになると、ルーターの無線キューで数百msの遅延が生じる。無線キューを改善すると、負荷中の遅延が約10分の1に減る - [Tests for Bufferbloat](https://www.bufferbloat.net/projects/bloat/wiki/Tests_for_Bufferbloat/) · Bufferbloat.net · Pingを実行したまま速度テストで回線を埋め、Pingが上がればバッファブロート。負荷中の遅延が50ms超(またはB評価未満)なら対策を推奨 #### hn-nat · NATマッピングの期限切れ · NAT mapping timeout ルーターは、しばらくパケットがやり取りされていないアイドル接続をNATテーブルから削除します。しばらく放置した後、動いた瞬間に切断されるときによくある原因です。 - なぜ → すると → 画面では:ルーターが「内側の機器 ↔ 外側のサーバー」の接続をNATテーブル(アドレス変換表)に記録 → パケットがしばらくないとテーブルから削除(UDPは30〜120秒が多い) → サーバーのパケットが家の中に入れず、切断 - 症状:切断 / 要因:パケットロス - 誰に:自分だけ, 同じ家 / いつ:しばらく放置した後 - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:最も短いアイドルタイムアウトの半分以下の間隔でハートビートを送る(UDPのマッピングは家の中から出ていくパケットでしか確実に更新されないため、クライアントが送る)、切れたら自動再接続。サーバー:ハートビートに応答し、一定時間受信がなければ先に接続を片付ける。マッピングが消えて外側のアドレス・ポートが変わっても、セッショントークン(接続時に受け取った確認用の番号)で同じプレイヤーであることを確かめて引き継ぐ。 - グラフでは:接続が一斉に切れる(切断数(ハートビートのタイムアウト)、切断前のアイドル時間) - 確認箇所:サーバーの切断理由と、切断前にその接続で最後のパケットがやり取りされてから経過した時間(アイドル時間)を集めて分布を確認。テストでは、UDPパケットの間隔を30秒・60秒・120秒と延ばしながら、応答が途切れる間隔を測定 - 該当する場合:放置していた接続だけが切れ、アイドル時間が30〜120秒のような特定の値の直後に集中する。ハートビートの間隔をそれより短くすると消える - 該当しない場合:動いている最中にも切れるなら回線・経路側。特定のモバイルキャリアだけで短い値に集中するなら「通信事業者の共有IP(CGNAT)」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [RFC 4787: Network Address Translation (NAT) Behavioral Requirements for Unicast UDP](https://www.rfc-editor.org/rfc/rfc4787) · IETF · UDPのマッピングタイマーは2分より前に切れてはならず、デフォルトは5分以上を推奨。内側から出ていくパケットによる更新は必須、外側から入ってくるパケットによる更新は任意 - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · 家庭用ルーター34機種を測定:UDPマッピングの保持は30〜691秒・中央値90秒で、半数以上が2分未満。TCPは中央値約60分 - [RFC 9308: Applicability of the QUIC Transport Protocol](https://www.rfc-editor.org/rfc/rfc9308) · IETF · 経路にNATがあるインターネットでは約30秒間隔のkeep-aliveが適切。それより頻繁だとトラフィック・電力の無駄 #### hn-router · ルーターの性能不足・過熱 · Router CPU / session table exhaustion 安価なルーターに数十台の機器、数千の接続が集中すると、ルーター自体が処理しきれなくなります。 - なぜ → すると → 画面では:数十台の機器、P2P・トレントが数千の接続を開く → ルーターのCPUとセッションテーブルが飽和 → パケット処理の遅延・パケットロス、新しい接続の失敗 - 症状:カクつき, 接続不可・無限ロード, 切断 / 要因:パケットロス, ジッター - 誰に:同じ家 / いつ:長時間稼働するほど, ときどきランダムに - 主担当:外部・外部 - 外部の対応:ユーザーに、ルーターの再起動(一時的な対処)、ルーターの交換、接続を多く開くプログラム(P2P・トレント)の整理を案内。 - グラフでは:上限で頭打ち(ルーターのCPU・接続数、ルーターまでのRTT) - 確認箇所:ルーターの管理画面でCPU使用率・接続(セッション)数・接続中の機器数を確認し(対応しているルーターなら)、ルーター自体までのPingを再起動の前後で比較 - 該当する場合:接続数が多いときにルーターまでのPingの段階で跳ねたりパケットロスが出たりし、新しい接続が失敗する。再起動するとしばらくは問題ないが、また悪くなる - 該当しない場合:ルーターまでは問題なく、その先だけ悪いなら回線・通信事業者の区間 - 確認手段:ユーザー側の環境で確認 - 出典: - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · 家庭用ルーターが1つのサーバーポートに対して許すTCP接続数は16〜約1,024個(中央値135)、安価な機器はスループットが数Mbps程度にとどまることもある - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Linuxの接続追跡テーブルの最大エントリ数(nf_conntrack_max)と、状態ごとの保持時間のデフォルト値 #### hn-handover · 基地局のハンドオーバー(移動中) · Cellular handover バスや地下鉄で移動すると、基地局が切り替わる間、通信が途切れます。 - なぜ → すると → 画面では:移動に伴い、接続する基地局が切り替わる → 通常は数十msの空白だが、電波が悪く切り替えに失敗すると数百ms〜数秒途切れることもある → 止まった後にワープ、長いと切断 - 症状:フリーズ, ワープ, 切断 / 要因:パケットロス - 誰に:自分だけ / いつ:移動中・マップ切り替え時 - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:短い途切れに耐えるタイムアウト、素早い再接続。サーバー:数秒途切れてもすぐには追い出さないタイムアウト、再接続したら同じセッションで引き継ぐ。 - 外部の対応:移動中(バス・地下鉄)の切断は基地局の切り替えが原因であることをユーザーに案内。 - グラフでは:途切れた後にまとめて到着(受信パケット数、RTT) - 確認箇所:切断の報告が移動中(バス・地下鉄)のものかを確認し、クライアントログにある受信の空白の時刻と、ネットワーク種別・電波の変化を確認 - 該当する場合:移動中だけ受信が数百ms〜数秒途切れた後にまとめて届き、止まっているときは再現しない - 該当しない場合:止まっていても同じなら「モバイル電波の弱さ・不感地帯」か「5G↔LTEの頻繁な切り替え(5Gエリアの境界)」 - 確認手段:ユーザー側の環境で確認 - 出典: - [Report ITU-R M.2134: Requirements related to technical performance for IMT-Advanced radio interface(s)](https://www.itu.int/pub/R-REP-M.2134-2008) · ITU · ハンドオーバー中にデータを送受信できない時間の要求値:同一周波数27.5ms、異なる周波数40〜60ms - [Understanding Operational 5G: A First Measurement Study on Its Coverage, Performance and Energy Consumption (SIGCOMM 2020)](https://xyzhang.ucsd.edu/papers/DXu_SIGCOMM20_5Gmeasure.pdf) · ACM · 商用網での実測ハンドオーバー遅延:4G↔4G平均30ms、5G(NSA)セル間平均108ms #### hn-rrc · RRC状態遷移の遅延(モバイル無線の省電力) · Radio state promotion (RRC) スマホはしばらく通信がないと無線接続を低電力状態に落とし、次のパケットのときに再び立ち上げるため遅れます。 - なぜ → すると → 画面では:しばらく通信がないと、スマホが無線接続を省電力状態に切り替える → 次のパケットを送るには、接続を再び立ち上げる必要がある → しばらく放置した後の最初の操作だけが特に遅い - 症状:入力遅延 / 要因:遅延 - 誰に:自分だけ / いつ:しばらく放置した後 - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:軽い定期送信でアクティブ状態を維持(バッテリー消費とのトレードオフ)。 - 数値の目安:LTEは通常10秒前後通信がないと省電力状態に落ち、復帰には数十〜数百msかかります(実測例:約0.3〜0.6秒)。3Gは1秒以上。 - グラフでは:一部だけ高い(アイドル後の最初のリクエストのRTT(モバイル)) - 確認箇所:ゲーム内のRTTを、直前の通信からの間隔ごとに分けて確認。モバイルで10秒以上休んだ後に送った最初のパケットと、連続して送ったパケットのRTTを比較 - 該当する場合:モバイル回線で休んだ後に送った最初のパケットだけが数百ms遅れ、すぐ続けて送ったパケットは正常。Wi-Fiでは差がない - 該当しない場合:連続して送っても遅いなら電波・回線・経路側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [An In-depth Study of LTE: Effect of Network Protocol and Application Behavior on Performance (SIGCOMM 2013)](https://conferences.sigcomm.org/sigcomm/2013/papers/sigcomm/p363.pdf) · ACM · 測定したLTE網の省電力移行タイマー(tail)は10秒、省電力状態からの復帰遅延は中央値435ms(25〜75%:319〜558ms)、3Gは約1.5〜2秒 - [Optimize network access](https://developer.android.com/develop/connectivity/network-ops/network-access-optimization) · Android (Google) · 無線状態の遷移遅延とtail時間は、無線技術(3G・LTE・5G)と通信事業者の設定によって異なる。3Gの例:低電力→最大電力で約1.5秒、待機→最大電力で2秒以上 - [Report ITU-R M.2134: Requirements related to technical performance for IMT-Advanced radio interface(s)](https://www.itu.int/pub/R-REP-M.2134-2008) · ITU · 待機状態からアクティブ状態に移る制御プレーンの遅延の要求値は100ms未満(ページング・有線区間を除く) #### hn-weak-cell · モバイル電波の弱さ・不感地帯 · Weak cellular signal エレベーター・地下・建物の奥では、再送が増えて速度が落ち、最終的に切断されます。 - なぜ → すると → 画面では:電波の弱い場所へ移動 → 無線区間の再送の増加、速度の低下、瞬断 → ジッター・パケットロスでカクつき・ワープ、最終的に切断 - 症状:カクつき, ワープ, 切断 / 要因:ジッター, パケットロス - 誰に:自分だけ / いつ:移動中・マップ切り替え時 - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:再接続の流れの改善、ネットワーク品質の表示。 - 外部の対応:電波の弱い場所(エレベーター・地下・建物の奥)で起きる問題であることをユーザーに案内。 - グラフでは:一部だけ高い(RTT・パケットロス(モバイルユーザー別)) - 確認箇所:切断報告時の場所(エレベーター・地下・建物の中)とスマホの電波表示を確認し、電波の良い場所で同じ操作を繰り返して比較 - 該当する場合:電波の弱い場所でだけRTT・パケットロスが増えて切れ、電波の良い場所に移ると消える - 該当しない場合:電波が良いのに同じなら通信事業者の区間かサーバー側 - 確認手段:ユーザー側の環境で確認 - 出典: - [An In-depth Study of LTE: Effect of Network Protocol and Application Behavior on Performance (SIGCOMM 2013)](https://conferences.sigcomm.org/sigcomm/2013/papers/sigcomm/p363.pdf) · ACM · LTEは無線区間のロスを物理層・MAC層の再送で隠す。利用可能な帯域幅は電波強度などによって秒単位で大きく変わる #### hn-5g-flip · 5G↔LTEの頻繁な切り替え(5Gエリアの境界) · 5G NSA / LTE switching 5Gの電波が弱い建物の中や5Gエリアの境界では、スマホが5GとLTEを頻繁に行き来し、切り替わるたびにPingが跳ねたり通信が一瞬途切れたりします。 - なぜ → すると → 画面では:5Gの電波が不安定な場所(建物の中、5Gエリアの境界)にいる → スマホが5GとLTEの間を頻繁に切り替え、そのたびに短い空白が生じる → 動かずにいても不規則にPingが跳ね、ときどきフリーズ・ワープ - 症状:カクつき, ワープ, フリーズ / 要因:ジッター, パケットロス - 誰に:自分だけ / いつ:ときどきランダムに, 移動中・マップ切り替え時 - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:ジッターが増えたら補間バッファの長さを自動で延ばす。ラグが起きたときのログにネットワーク種別(5G・LTE)の変化もあわせて記録し、原因を区別。 - 外部の対応:ユーザーに、設定でLTE優先モードに切り替えて比較してみるよう案内、Wi-Fiの使用を推奨。 - 数値の目安:切り替え1回あたり数十〜数百ms。韓国の5Gは大半がLTEと組み合わせて使う方式(NSA)のため、5G側だけがつながったり外れたりしやすくなっています。 - グラフでは:不定期なスパイク(RTT、ネットワーク種別(5G・LTE)の変化) - 確認箇所:スマホの設定をLTE優先に切り替え、同じ場所で比較。クライアントがAndroidのTelephonyDisplayInfoのネットワーク表示(OVERRIDE_NETWORK_TYPE_NR_NSAなど)の変化をRTTとあわせて記録すれば、より確実 - 該当する場合:RTTが跳ねた時刻と5G↔LTEの表示が変わった時刻が重なり、LTE優先モードではスパイクが消える - 該当しない場合:ネットワーク表示が変わらないのに跳ねるなら「モバイル電波の弱さ・不感地帯」か回線側 - 確認手段:ユーザー側の環境で確認 - 出典: - [Understanding Operational 5G: A First Measurement Study on Its Coverage, Performance and Energy Consumption (SIGCOMM 2020)](https://xyzhang.ucsd.edu/papers/DXu_SIGCOMM20_5Gmeasure.pdf) · ACM · NSAの5Gは制御をLTEに任せるため、5Gのセルを切り替えるときは5Gを離し、LTEを経由して再びつながる:平均108ms(4G→5Gは80ms)、5Gが絡む切り替えの直後はTCPスループットが73〜83%低下 - [5G 통신서비스 품질평가 결과 발표](https://www.korea.kr/briefing/policyBriefingView.do?newsId=156404679) · 과학기술정보통신부 · 2020年の発表時点で、韓国の5GはNSA方式で提供されており、SAへの移行は予定段階 - [TelephonyDisplayInfo](https://developer.android.com/reference/android/telephony/TelephonyDisplayInfo) · Android (Google) · OVERRIDE_NETWORK_TYPE_NR_NSA:LTEに接続しながら、5G(NR)とのデュアルコネクティビティ(EN-DC)が可能な場合、または接続している場合のネットワーク表示 #### hn-captive · 公衆Wi-Fi・社内ネットワークの制限 · Captive portal, restrictive network カフェのWi-Fiのログインページや会社のファイアウォールが、ゲームの接続をブロックします。 - なぜ → すると → 画面では:ログインページでの認証前か、ファイアウォールがゲームのポート・UDPを遮断 → 接続の試み自体がブロックされるか、一部だけが通る → 接続不可、ログインはできるのにゲームに入れない - 症状:接続不可・無限ロード / 要因:パケットロス - 誰に:自分だけ / いつ:接続直後・メンテ明け - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:ブロックされたときに理由を伝える案内(ログインページでの認証前、UDPの遮断など)、UDPがブロックされたら代替経路へ自動で切り替え。サーバー:TCP 443などの代替経路を提供。 - 外部の対応:ユーザーに、公衆Wi-Fiではまずログインページで認証し、社内ネットワークのように制限された場所では別のネットワークを使うよう案内。 - グラフでは:一部だけ高い(接続失敗数(ネットワーク別)) - 確認箇所:失敗したユーザーにモバイルデータ通信など別のネットワークに切り替えて接続してもらい、サーバーの接続ログで、UDPの最初のパケットが届いたかと、TCP 443の代替経路ならつながるかを確認 - 該当する場合:特定のWi-Fi(カフェ・会社)でだけ失敗し、ほかのネットワークではすぐつながる。ログインページでの認証前か、UDPだけがサーバーに届かない - 該当しない場合:どのネットワークでも失敗するならアカウント・サーバー・「DNSの障害・遅延」側。特定の国・通信事業者全体で失敗するなら「国・通信事業者単位のUDP制限・パケット検査」 - 確認手段:ユーザー側の環境で確認 - 出典: - [RFC 8952: Captive Portal Architecture](https://www.rfc-editor.org/rfc/rfc8952) · IETF · キャプティブポータル:規約への同意・認証などの条件を満たすまで接続が制限されるネットワーク - [RFC 9308: Applicability of the QUIC Transport Protocol](https://www.rfc-editor.org/rfc/rfc9308) · IETF · 測定研究によるとネットワークの3〜5%はUDPをすべてブロックするため、UDPベースのアプリはTCP(TLS)の代替経路を用意する必要がある ### L4 インターネット回線(原因14件) #### isp-distance · 伝搬遅延(物理的な距離) · Propagation delay 光ファイバーの中では、光でさえ1秒に約20万kmしか進みません。遠いサーバーは、どれほど性能が良くても遅れます。 - なぜ → すると → 画面では:サーバーが遠くにある(海外サーバー、別の大陸) → 距離の分だけ往復時間が延びる(1,000kmあたり最低10ms) → すべての操作に一定の入力遅延、判定で不利 - 症状:入力遅延 / 要因:遅延 - 誰に:特定の地域・ISP / いつ:常に - 主担当:インフラチーム・サーバーインフラ / 副担当:インフラチーム・ネットワークインフラ, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:物理法則はコードでは直せないため緩和のみ可能。近い地域のサーバーを選ばせる地域選択、ラグコンペンセーション(巻き戻し)による判定の不利の軽減。 - インフラチームの対応:サーバー機器・OS:利用者の多い地域ごとにサーバーを置く。ネットワーク:近くに接続拠点(エッジ)を置く、遠回りの少ない回線・経路を選ぶ。 - 数値の目安:ソウル・東京間は約30ms、ソウル・シンガポール間は約75ms、ソウル・米国西海岸間は約140ms、ソウル・欧州間は約230〜270msです(往復、実際の経路での値)。欧州へは直線上に大容量のケーブルがほとんどなく、東南アジア・スエズ経由か米国経由で回り込むため、距離から見込むよりずっと長くなります。 - グラフでは:最初から常に高い(RTT(国・地域別)) - 確認箇所:接続元IPに国情報を付けて国別のRTT分布を確認し、その地域のクラウドリージョンのVMやRIPE Atlasのプローブ(国・ASNで選択)からサーバーまでping・tracerouteで測定 - 該当する場合:遠い国のRTTが時間帯に関係なく常に高く、その値が距離から計算した最小遅延(1,000kmあたり往復10ms)や公開されている遅延統計に近い - 該当しない場合:距離で説明できる値よりずっと高ければ「迂回ルーティング」、夜だけ上がるなら「ピーク時間帯のピアリング混雑」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 実際の事例:riot-direct-2015 - 出典: - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · 光ファイバーの伝搬遅延の計画値5µs/km(秒速約20万km、1,000kmの往復で10ms) - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · ソウル(Korea Central)を起点とした実測の往復中央値:東京30ms、シンガポール68ms、米国西海岸124〜136ms、欧州234〜244ms - [AAE-1 & SMW5 cable cuts impact millions of users across multiple countries](https://blog.cloudflare.com/aae-1-smw5-cable-cuts/) · Cloudflare · 欧州・アジア間のトラフィックは、大半がエジプト(スエズ)を通る海底ケーブルを経由 - [Probe Selection (RIPE Atlas REST API)](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/creating-measurements/probe-selection/) · RIPE NCC · RIPE Atlasの測定で、プローブを国・地域・ASN・アドレス帯で選んでping・tracerouteを実行 #### isp-satellite · 衛星インターネット(低軌道・静止軌道) · Satellite internet (LEO, GEO) 衛星インターネットは電波が宇宙を往復するため、静止軌道衛星では往復だけで0.5秒を超えます。Starlinkのような低軌道衛星は普段は速いものの、経路を割り当て直す瞬間に遅延が揺れ、一瞬途切れることもあります。 - なぜ → すると → 画面では:自宅・船・飛行機から、静止軌道衛星や低軌道衛星のインターネット、衛星を使う機内Wi-Fiで接続 → 静止軌道は高度が約36,000kmあり、往復する距離そのものが長い。低軌道は端末・衛星・地上局の経路を短い周期で割り当て直し、その瞬間に遅延・パケットロスが一時的に発生 → 静止軌道はすべての操作に大きな入力遅延。低軌道は普段は問題ないが、一定の間隔でカクつき・ワープ - 症状:入力遅延, カクつき, ワープ / 要因:遅延, ジッター, パケットロス - 誰に:自分だけ, 同じ家, 特定の地域・ISP / いつ:常に, 一定の周期で - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:ジッターに合わせて補間バッファの長さを自動で延ばす、短いパケットロスに耐えられるよう入力を重複送信、接続品質の表示。サーバー:判定の受付時間やラグコンペンセーションの上限を決めるときに衛星回線の遅延を考慮、1秒前後の途切れですぐに切断しないタイムアウト。 - 外部の対応:ユーザーに、衛星インターネットは遅延が大きかったり周期的に跳ねたりすることがあると案内、対戦コンテンツではできるだけ地上の有線回線を使うよう案内。 - 数値の目安:静止軌道(高度36,000km)は、電波が宇宙を通過するだけで片道260ms、往復で520msを超えます(ITU-T G.114)。低軌道のStarlinkは、公式資料(15秒平均値)で米国のピーク時間帯の中央値が33ms、最も悪い1%(p99)でも65ms未満です(2024年)。測定研究では、15秒ごとに経路を割り当て直す瞬間に遅延が変わり、1秒未満の短い途切れが発生していました。2018年の機内インターネットの測定では、衛星方式の往復遅延は平均750msでした。 - グラフでは:一部だけ高い(RTT・ジッター(衛星インターネット事業者のASN別)) - 確認箇所:接続元IPのASNが衛星インターネット事業者かを確認し、その事業者のユーザーだけでRTT分布と時系列を描画。そのASNのRIPE Atlasプローブからサーバーまでpingを数分間続けて測定するか、ユーザーにpingを実行したままにしてもらい、跳ねる間隔を測定 - 該当する場合:静止軌道の事業者はRTTが常に500msを超え、低軌道の事業者は普段は数十msで、約15秒間隔でRTTが変わるか短く途切れる - 該当しない場合:衛星事業者ではないのにRTTが常に高ければ「伝搬遅延(物理的な距離)」か「迂回ルーティング」、不規則に跳ねるならWi-Fi・モバイルの電波側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:低軌道衛星は衛星までの距離が短いため(Starlinkの1区間で1.8〜3.6ms)、普段の遅延は地上回線と同程度になることもあります。ただし、地上局からインターネットに出る地点(PoP)がゲームサーバーから遠ければ、その分だけ経路が長くなり、衛星間のレーザーリンクを経由するとさらに遅延が加わります。測定研究では、15秒周期の揺れは衛星間の切り替えとは関係がなく、世界中で同じ時刻に行われる経路の再割り当てが原因だとみています。機内Wi-Fiは方式(衛星、地上の基地局)によって遅延が大きく異なり、静止軌道衛星を使う方式なら上記と同じ長い往復遅延が発生します。 - 出典: - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · 衛星区間の片道伝搬遅延の計画値:高度400kmで12ms、14,000kmで110ms、36,000km(静止軌道)で260ms - [Improving Starlink’s Latency](https://starlink.com/public-files/StarlinkLatency.pdf) · Starlink · 米国ピーク時間帯の中央値48.5ms→33ms、最も遅い1%(p99)150ms超→65ms未満(2024年)、衛星1区間の伝搬1.8〜3.6ms、レーザーリンクを経由すると遅延が加わり、地上局からインターネット接続点(PoP)までの距離も遅延の要因 - [A Multifaceted Look at Starlink Performance (WWW 2024)](https://www.nitindermohan.com/documents/2024/pubs/starlinkWWW2024.pdf) · ACM · Starlinkは15秒ごとに世界中で同じ時刻に経路を割り当て直し、その境目で遅延・スループットが揺れ、1秒未満の短い途切れが発生(衛星間の切り替えが原因ではない)、端末↔衛星↔地上局の区間の遅延は約40ms - [Mile High WiFi: A First Look at In-Flight Internet Connectivity (WWW 2018)](https://aqualab.cs.northwestern.edu/publication/2018/jrula-www18/) · ACM · 機内インターネットの45時間の測定:往復遅延の平均は地上基地局方式で200ms、衛星方式で750ms、衛星方式のパケットロス率の中央値は7% - [Probe Selection (RIPE Atlas REST API)](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/creating-measurements/probe-selection/) · RIPE NCC · RIPE Atlasの測定で、プローブを国・地域・ASN・アドレス帯で選んでping・tracerouteを実行 #### isp-routing · 迂回ルーティング · Suboptimal routing 通信事業者どうしの接続契約の都合で、近いサーバーでも遠くを回って届きます。 - なぜ → すると → 画面では:自分の通信事業者とサーバー側の通信事業者が直接つながっていない → 別の国や別の都市を経由し、距離と経由する機器が増える → 特定の通信事業者のユーザーだけPingが際立って高い - 症状:入力遅延 / 要因:遅延 - 誰に:特定の地域・ISP / いつ:常に - 主担当:インフラチーム・ネットワークインフラ / 副担当:外部・外部 - インフラチームの対応:複数の通信事業者と接続(マルチホーム)、通信事業者別のPingを監視して遠回りしている事業者を特定、通信事業者と経路の調整を協議。 - 外部の対応:該当する通信事業者に経路の調整を依頼。 - 数値の目安:同じ国の中でも、経路によってPingが2〜3倍違うことがあります。 - グラフでは:最初から常に高い(RTT(通信事業者・ASN別)) - 確認箇所:通信事業者(ASN)別にRTTを比較し、遅い事業者のRIPE Atlasプローブやユーザーから受け取ったtraceroute・mtrで、経路がどの国・都市を経由しているかを確認。IPv4とIPv6は別々に測定(mtr -4、-6) - 該当する場合:同じ地域なのに特定の通信事業者だけが常に高く、経路に別の国や遠い都市を経由する区間がある。または一方のアドレスファミリー(IPv4かIPv6)だけが高い - 該当しない場合:すべての通信事業者が同じように高ければ「伝搬遅延(物理的な距離)」、夜だけ高ければ「ピーク時間帯のピアリング混雑」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:IPv4とIPv6は経路が別々に決まるため、同じサーバーでも片方だけが遠回りして遅くなることがあります(2016年のAPNICの測定:同じ通信事業者の中に、IPv6がIPv4より15・25・75ms遅いユーザー集団がそれぞれ現れた)。IPv6とIPv4のうち先につながった方を使うHappy Eyeballs(RFC 8305)方式のアプリは、IPv6を先に試し、IPv6が推奨値の250ms以内につながればIPv4は試しません。そのため、IPv6側が少し遅くても、その経路で接続しやすくなります。特定の通信事業者だけPingが高いときは、IPv4とIPv6を分けて測定します。 - 実際の事例:riot-direct-2015 - 出典: - [Quantifying the Causes of Path Inflation (SIGCOMM 2003)](https://conferences.sigcomm.org/sigcomm/2003/papers/p113-spring.pdf) · ACM · 65のISPを分析:ISP間のピアリングポリシーとドメイン間ルーティングが経路を大きく延ばす - [The Internet at the Speed of Light (HotNets 2014)](https://conferences.sigcomm.org/hotnets/2014/papers/hotnets-XIII-final111.pdf) · ACM · 実際のルーター経路は光ファイバーの直線距離と比べて中央値で約1.5倍、近い2地点間のパケットが地球の反対側を回る例(ヘアピン)もある - [Probe Selection (RIPE Atlas REST API)](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/creating-measurements/probe-selection/) · RIPE NCC · RIPE Atlasの測定で、プローブを国・地域・ASN・アドレス帯で選んでping・tracerouteを実行 - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · -4・-6でIPv4・IPv6だけを使って経路を測定 - [RFC 8305: Happy Eyeballs Version 2: Better Connectivity Using Concurrency](https://www.rfc-editor.org/rfc/rfc8305) · IETF · アドレスやアドレスファミリー(IPv4・IPv6)は、ネットワークによってはブロック・故障・低速の場合がある、IPv6を先に試し、次の接続試行まで推奨250ms待つ - [IPv6 Performance – Revisited](https://blog.apnic.net/2016/08/22/ipv6-performance-revisited/) · APNIC · 同じデュアルスタックのユーザーでIPv6・IPv4の往復時間を比較:アクセス網によってIPv6パケットの扱いがまったく異なることがあり、同じ通信事業者の中にIPv6が15・25・75ms遅い集団が現れる #### isp-peak · ピーク時間帯のピアリング混雑 · Peak-hour congestion at peering 夜9〜11時ごろは動画のトラフィックが急増し、通信事業者間の接続区間(ピアリング)が混雑しやすくなります。 - なぜ → すると → 画面では:夜の時間帯にストリーミング・ダウンロードが集中 → ピアリング区間でキューの滞留とパケットロスが発生 → 夜だけ、特定の通信事業者のユーザーにカクつき・ワープ - 症状:カクつき, ワープ, 引き戻し / 要因:ジッター, パケットロス, 遅延 - 誰に:特定の地域・ISP / いつ:夜のピーク時間帯 - 主担当:インフラチーム・ネットワークインフラ / 副担当:外部・外部 - インフラチームの対応:該当する通信事業者との直接接続を増やす、混雑した経路の迂回、通信事業者別に夜間のパケットロス・Pingを監視。 - 外部の対応:該当する通信事業者にピアリング区間の増強を依頼。 - グラフでは:特定の時間帯だけ高い(RTT・パケットロス(通信事業者別)) - 確認箇所:通信事業者(ASN)別にRTT・パケットロスを時間帯ごとに描画し、その事業者のRIPE Atlasプローブやユーザーから夜と昼それぞれのmtrを受け取り、パケットロスが始まる区間を確認 - 該当する場合:特定の通信事業者だけ毎晩9〜11時ごろにRTT・パケットロスが上がり、mtrで通信事業者間の接続区間から宛先までパケットロス・遅延が続く - 該当しない場合:すべての通信事業者で一緒に上がるなら自社の回線・サーバー側。同じ家だけ夜に悪ければ「Wi-Fiチャンネルの混雑」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Inferring Persistent Interdomain Congestion (SIGCOMM 2018)](https://www.caida.org/catalog/papers/2018_inferring_persistent_interdomain_congestion/inferring_persistent_interdomain_congestion.pdf) · ACM · 通信事業者間の接続区間の一部で、毎日ピーク時間帯に遅延が上がる混雑が繰り返され、混雑する時間帯にはパケットロス率も上昇 - [Probe Selection (RIPE Atlas REST API)](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/creating-measurements/probe-selection/) · RIPE NCC · RIPE Atlasの測定で、プローブを国・地域・ASN・アドレス帯で選んでping・tracerouteを実行 #### isp-cable · 海底ケーブル・国際回線の障害 · Submarine cable fault 海底ケーブルが切れると、修理されるまでの数週間(長ければ数か月)は遠い迂回経路を回ることになり、残った回線は混雑します。 - なぜ → すると → 画面では:ケーブルの切断・機器の故障 → トラフィックが遠い迂回経路と残った回線に集中 → 海外からの接続者でPingの急上昇とパケットロスが数日〜数週間続く - 症状:入力遅延, ワープ / 要因:遅延, パケットロス - 誰に:特定の地域・ISP / いつ:常に - 主担当:外部・外部 / 副担当:インフラチーム・ネットワークインフラ - インフラチームの対応:別経路の回線の確保、障害時にトラフィックをその経路へ移す。 - 外部の対応:海外からの接続者に原因と復旧見込みを告知、回線事業者に復旧スケジュールの確認を依頼。 - グラフでは:ある時点から階段状に上昇(RTT(海外の国別)) - 確認箇所:国別のRTT・パケットロスのグラフで上昇した時刻を探し、Cloudflare Radarのインターネット障害のまとめや海底ケーブル事業者の告知と照合。tracerouteで経路が別の大陸を回っていないか確認 - 該当する場合:ある時点から特定の海外地域のRTTが一段上がって数日〜数週間とどまり、同じ時期にケーブル障害の報告がある。経路が普段と違う遠い迂回経路に変わっている - 該当しない場合:数日以内に元に戻り、障害報告がなければ「BGPの経路変更・収束」か通信事業者の区間 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [AAE-1 & SMW5 cable cuts impact millions of users across multiple countries](https://blog.cloudflare.com/aae-1-smw5-cable-cuts/) · Cloudflare · 海底ケーブルの修理には修理船の派遣が必要で、通常数日〜数週間(トンガの事例は38日)、切断時は欧州・アジア間の遅延・パケットロスが増加 - [Q2 2024 Internet disruption summary](https://blog.cloudflare.com/q2-2024-internet-disruption-summary/) · Cloudflare · 2024年2月に損傷した紅海のケーブルは7月時点でも修理中(紛争地域)、5月のEASSy・Seacomの切断は19日で復旧 - [Q1 2024 Internet disruption summary](https://blog.cloudflare.com/q1-2024-internet-disruption-summary/) · Cloudflare · 西アフリカのケーブル切断(3月14日)は3〜6週間後に復旧、その間は別のケーブルにトラフィックを移した #### isp-bgp · BGPの経路変更・収束 · Route change / BGP convergence インターネットの経路情報が変わり、再び収束するまでの数秒〜数十秒(まれに数分)の間、パケットが失われます。 - なぜ → すると → 画面では:どこかの通信事業者の区間で経路情報が変わる → 数秒〜数十秒の間パケットが消えるか、新しい経路に切り替わる → 突然数秒止まった後、Pingの値が変わる(例:40→70ms) - 症状:フリーズ, ワープ / 要因:パケットロス, 遅延 - 誰に:特定の地域・ISP / いつ:ときどきランダムに - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発, 外部・外部 - ゲーム開発チームの対応:短い途切れに耐えるタイムアウト(数秒止まった接続をすぐに切らない)。 - インフラチームの対応:経路の監視(自社IPアドレス帯の経路・Pingの変化を監視)、自社回線の障害はBFDで1秒以内に検知して切り替え(BGPのデフォルトのホールドタイムは90〜180秒)、遠い経路に変わったまま戻らなければ別の回線にトラフィックを移す。 - 外部の対応:経路が頻繁に変わる通信事業者の区間は、その通信事業者に原因の確認を依頼。 - グラフでは:ある時点から階段状に上昇(RTT、tracerouteの経路) - 確認箇所:RTTが変わった時刻の前後でtraceroute・mtrの経路を比較し、RIPEstatのBGPlayで自社アドレス帯(prefix)のBGP経路変更の履歴を確認 - 該当する場合:数秒の停止と同時にRTTが別の値に移り、同じ時刻にBGPアップデートとAS経路の変更がある - 該当しない場合:経路変更の履歴がないのに夜だけ上がるなら「ピーク時間帯のピアリング混雑」、一部の接続だけ悪ければ「ECMP経路のうち1本の不良」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 実際の事例:cloudflare-2020, meta-2021, cloudflare-dns-2025 - 出典: - [RFC 4271: A Border Gateway Protocol 4 (BGP-4)](https://www.rfc-editor.org/rfc/rfc4271) · IETF · BGPのホールドタイムの推奨デフォルト値は90秒(この時間内に相手からメッセージがなければセッションを切断) - [BGP updates in 2024](https://blog.apnic.net/2025/01/07/bgp-updates-in-2024/) · APNIC · 不安定になった経路が再び安定するまで、1日平均で25〜35秒(IPv4)、40〜50秒(IPv6) - [Delayed Internet Routing Convergence (SIGCOMM 2000)](https://conferences.sigcomm.org/sigcomm/2000/conf/paper/sigcomm2000-5-2.pdf) · ACM · 経路障害の後、収束に数分かかることがあり、その間はパケットロス・遅延が増える(2000年当時の測定) - [BGPlay (RIPEstat Data API)](https://stat.ripe.net/docs/data-api/api-endpoints/bgplay) · RIPE NCC · アドレス帯(prefix)の開始時点のBGP経路と、その期間に観測されたBGPアップデート、経路上のAS情報を表示 #### isp-ecmp · ECMP経路のうち1本の不良 · ECMP / link bundle member fault 通信事業者やデータセンターは同じ宛先への経路を複数持ち、接続ごとに1本の経路を決めて送ります。1本の経路だけが故障すると、その経路に割り当てられた人だけにラグが出続けます。 - なぜ → すると → 画面では:複数の回線を束ねた区間で、1本の回線や1台の機器が不良、または混雑 → アドレス・ポートの組み合わせ(ハッシュ)で経路が決まり、その経路に割り当てられた接続だけにパケットロス・遅延 → 同じ地域・同じ通信事業者なのに一部の人だけが継続的にワープ。再接続すると直ることもある - 症状:ワープ, 引き戻し, カクつき / 要因:パケットロス, ジッター - 誰に:自分だけ, 特定の地域・ISP / いつ:常に - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発, 外部・外部 - ゲーム開発チームの対応:接続ごとのパケットロス・再送の統計を残し、影響を受けている人のIP・ポートと時刻を抽出できるようにする(TCPはTCP_INFOの再送回数、UDPは抜けたパケットのシーケンス番号から計算)。 - インフラチームの対応:影響を受けている人のIP・ポートと時刻を集めて通信事業者・データセンターに伝える、経路ごとのパケットロス監視、経路の測定はゲームと同じプロトコル・ポートで行う(mtr --tcp・--udpと--port)、自社機器の経路なら不良の回線・機器を束から外す。 - 外部の対応:通信事業者に不良経路の確認・交換を依頼、ユーザーには再接続で一時的に回避するよう案内(再接続でポートが変わる場合)。 - 数値の目安:経路が4本なら、影響を受けるのはユーザーの約4分の1だけです。Pingの測定はゲームとは別の経路を通るため、正常な値が出ることもあります。 - グラフでは:一部だけ高い(接続ごとのパケットロス・再送(IP・ポート別)) - 確認箇所:接続ごとのパケットロス・再送を送信元IP・ポートで分けて確認。mtrをUDP(-u)でゲームのポート(-P)宛てに送り、送信元ポート(-L)を固定して測定し、送信元ポートを変えて何度か繰り返す。-Lを付けずに-Pだけを指定すると、要求ごとに送信元ポートが変わって複数の経路が混ざる - 該当する場合:同じ地域・通信事業者の中で、特定の送信元ポート(またはアドレス)の組み合わせだけで継続的にパケットロスが発生し、再接続してポートが変わると直る - 該当しない場合:ポートを変えてもすべて悪ければ、ある区間全体の混雑・障害 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:1つの接続のパケットの順序が入れ替わらないよう、機器はアドレスとポート(機器の設定によってはアドレスだけ)から計算した値で、接続ごとに経路を固定します(ECMP、LAG)。アドレスだけを使う環境では、再接続しても同じ経路になるため改善しません。そのため「Pingは正常なのにゲームだけラグい」「再接続したら直った」といった報告が一緒に届いたら、この原因を疑います。 - 出典: - [RFC 7424: Mechanisms for Optimizing Link Aggregation Group (LAG) and Equal-Cost Multipath (ECMP) Component Link Utilization in Networks](https://www.rfc-editor.org/rfc/rfc7424) · IETF · LAG・ECMPはヘッダーフィールドのハッシュでフローごとにリンクを1本決め、パケットの順序を守る(フロー→リンクの多対一の対応) - [RFC 2991: Multipath Issues in Unicast and Multicast Next-Hop Selection](https://www.rfc-editor.org/rfc/rfc2991) · IETF · フローを区別する基準は実装によって異なる(宛先アドレスのみ、アドレスの組、ポートまで)、マルチパスではping・tracerouteの結果を信用しにくい - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · -u(UDP)、-P(宛先ポート)、-L(UDPの送信元ポート)オプション、-Pだけを指定すると送信元ポートに要求の通し番号が入り、要求ごとに変わる #### isp-shaping · 通信事業者の速度制限・トラフィック管理 · Traffic shaping, data caps データ使用量の上限を超えた場合や、特定のトラフィックを管理する料金プランでは、パケットが遅らされたり破棄されたりします。 - なぜ → すると → 画面では:料金プランのデータ容量を使い切った後の速度制限、または特定トラフィックの制限 → パケットが待たされるか破棄される → 一定の使用量を超えた後にラグ、特にモバイル - 症状:入力遅延, ワープ / 要因:遅延, パケットロス - 誰に:自分だけ, 特定の地域・ISP / いつ:常に, 夜のピーク時間帯 - 主担当:外部・外部 / 副担当:ゲーム開発チーム・サーバー開発, インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:ゲームのトラフィックを減らす(圧縮、必要なものだけ送信)。 - インフラチームの対応:特定の通信事業者だけでゲームのトラフィックが遅らされたり破棄されたりしていれば、データを集めて通信事業者にエスカレーション。 - 外部の対応:ユーザーに、料金プランのデータ容量を使い切って速度制限がかかっていないか、同じスマホでほかのアプリを使っていないかを確認するよう案内、通信事業者にゲームトラフィックの制限の有無を問い合わせ。 - 数値の目安:韓国のモバイル料金プランでは、データ容量を使い切ると通常1〜5Mbps、安いプランでは数百kbpsに制限されます。ゲーム自体の通信量は少なくても、同じスマホのほかのアプリが通信すると、速度制限装置の手前にキューができます。 - グラフでは:上限で頭打ち(スループット、RTT) - 確認箇所:ユーザーにキャリアのアプリで残りのデータ容量と速度制限の有無を確認してもらい、速度テストで最大速度を確認。サーバー側では通信事業者別のパケットロス・RTTを比較 - 該当する場合:スループットが1〜5Mbpsや数百kbpsといった一定の値から上がらず、その状態で同じスマホのほかのアプリが通信するとRTT・パケットロスが増える。データ容量を追加するかWi-Fiに切り替えると消える - 該当しない場合:速度制限がないのに特定の通信事業者だけ悪ければ「ピーク時間帯のピアリング混雑」か「迂回ルーティング」 - 確認手段:ユーザー側の環境で確認 - 出典: - [SKT, 고객 선택권과 혜택 강화한 신규 5G 요금제 출시](https://news.sktelecom.com/180213) · SK텔레콤 · 5G料金プランの基本データ容量を使い切った後の速度制御の例:最大400kbps、1Mbps、3Mbps - [SKT, 요금제 개편](https://news.sktelecom.com/225487) · SK텔레콤 · 基本データ容量を使い切った後も最大400kbpsで使い続けられる(全国民安心データ) #### isp-udp-block · 国・通信事業者単位のUDP制限・パケット検査 · UDP blocking, throttling and inspection by networks 一部のネットワークでは、特定のUDPアドレス・ポートをブロックしたりUDPの速度を制限したりし、パケット検査装置が識別できないプロトコルを遮断します。UDPで通信するゲームは、そのネットワークでは接続できなかったり、頻繁に切断されたりします。 - なぜ → すると → 画面では:UDPの速度を制限する一部の通信事業者網や、国・通信事業者単位のトラフィック検査(検閲)装置があるネットワークから接続 → 特定のUDPアドレス・ポートをブロック、混雑する時間帯にUDPの速度を制限、許可リストにないポート・プロトコルを遮断、または最初の数パケットだけ通してからブロック → 特定の国・通信事業者のユーザーだけ接続不可・無限ロード、接続してもすぐに切断、混雑する時間帯にパケットロスでワープ - 症状:接続不可・無限ロード, 切断, ワープ / 要因:パケットロス - 誰に:特定の地域・ISP / いつ:接続直後・メンテ明け, 常に, 夜のピーク時間帯 - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発, ゲーム開発チーム・サーバー開発, インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:クライアント:UDPが数秒以内につながらなければTCP・TLS 443の代替経路に自動で切り替える、最初はつながってもすぐ切れるケースも検知して代替経路で再試行、どの経路で接続したかをログに残す。サーバー:同じゲームプロトコルをTCP 443(TLS)でも受け付ける、代替経路は遅延が増えることがあるのでタイムアウトを調整。 - インフラチームの対応:海外の国・地域を追加する前に、現地の通信事業者網でUDPの到達可否とピーク時間帯のパケットロスを測定、TCP 443の代替経路を受けるリレー・ゲートウェイを現地の近くに置く、国・ASN別にUDP・TCPの接続成功率を監視、UDPの速度制限が確認された通信事業者はデータを集めてエスカレーション。 - 外部の対応:該当する通信事業者・機関にUDP制限の基準と緩和について問い合わせ、ユーザーには別のネットワークから接続して比較してみるよう案内。 - 数値の目安:IETFの文書が引用した測定によると、ネットワークの3〜5%はUDPを完全にブロックしています。Googleが2016年にQUIC(UDPベース)の利用状況を調べたところ、クライアントの4.4%はUDP・QUICがブロックされているか経路MTUが小さいために使えず、その大半は企業のファイアウォールの内側でした。通信事業者全体でブロックしている例は見られませんでした。0.3%はピーク時間帯にパケットロスが大きく増える、UDPの速度を制限しているとみられるネットワークにあり、通信事業者に要請して2015年の1%から減らしました。 - グラフでは:一部だけ高い(UDP接続成功率(国・ASN別)) - 確認箇所:国・ASN別にUDPの接続成功率とTCP 443の代替経路の成功率を分けて確認。該当する通信事業者網のクラウドVMやユーザーのPCから、ゲームのUDPポートとTCP 443でそれぞれ接続試験を行い、mtr -u -P(ゲームのポート)とmtr -T -P 443で、どの区間から応答が消えるかを比較 - 該当する場合:特定の国・ASNだけでUDPの最初の応答がないか数秒で切れ、同じ場所からのTCP 443は正常。速度制限の場合は、ピーク時間帯にだけUDPのパケットロスがはっきり増え、TCPへの影響は小さい - 該当しない場合:TCPも一緒に失敗するなら経路障害・IPブロック・「DNSの障害・遅延」側。すべての国で同じなら自社のサーバー・ファイアウォールの設定、UDP・TCPを問わず瞬間的な送信量が多いときだけパケットロスが出るなら「ポリサーによる超過分の破棄」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:パケット検査装置は、アドレス・ポート・プロトコルでUDPのフローを選んでブロックしたり、許可したプロトコル以外はすべてブロックする方式(許可リスト)を使ったりします(IRTFの調査文書)。装置がパケットの一部のフィールドだけを見て判断していると、プロトコルを少し変えただけでブロックされることもあります。QUICの初期には、あるファイアウォールがヘッダーの1ビットが変わった後、最初の数パケットは通してそれ以降のパケットをブロックしたため、クライアントがTCPに切り替えて接続するロジックが働きませんでした。海外の国・地域を追加したときに、「国内は問題ないのに、その国の一部の通信事業者だけ接続できない」という報告で発覚することがあります。カフェや会社のように、ある場所のネットワークだけでブロックされるなら「公衆Wi-Fi・社内ネットワークの制限」の項目を参照してください。 - 出典: - [RFC 9308: Applicability of the QUIC Transport Protocol](https://www.rfc-editor.org/rfc/rfc9308) · IETF · 測定研究によるとネットワークの3〜5%がUDPを完全にブロックしており、UDPベースのアプリは接続失敗を受け入れるか、TCP(TLS)の代替経路を用意する必要がある、登録されたサービスに対応しないポートはファイアウォールにブロックされることがある - [The QUIC Transport Protocol: Design and Internet-Scale Deployment (SIGCOMM 2017)](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/46403.pdf) · ACM · 2016年:クライアントの4.4%がQUIC(UDP)を使えなかった(UDP・QUICのブロックまたは小さい経路MTU、主に企業のファイアウォールの内側、通信事業者全体のブロックは観測されず)、0.3%はUDPの速度制限とみられるネットワーク(ピーク時間帯のパケットロス増加、通信事業者に要請して2015年の1%から減少)、ヘッダーの1ビット変更後に最初の数パケットだけを通し、それ以降をブロックしたファイアウォールがTCPへの切り替えロジックを無効にした事例 - [RFC 9505: A Survey of Worldwide Censorship Techniques](https://www.rfc-editor.org/rfc/rfc9505) · IRTF · ネットワークの検査装置はアドレス・ポート・プロトコルでTCP・UDPのフローを選んでブロックでき(QUICではUDPエンドポイントのブロックが観測された)、許可したプロトコル以外をブロックする方式は過剰なブロックを招き、特定のトラフィックの速度を制限する方式も使われる - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · -uでUDP、-TでTCP SYNを送り、-Pで宛先ポートを指定して、ゲームと同じプロトコル・ポートで経路を測定 #### isp-line · 回線品質の不良 · Faulty last-mile line / modem 端子の接触不良や古いケーブル、モデムの異常は、継続的なパケットロスと周期的な回線断を引き起こします。 - なぜ → すると → 画面では:ケーブルの損傷、接触不良、モデム・ONU(光回線終端装置)の異常 → ビットエラーでパケットが破棄され、ときどき回線の再接続で数秒〜1分ほど途切れることもある → 継続的な少量のパケットロス、ときどき数秒のフリーズや切断 - 症状:ワープ, フリーズ, 切断 / 要因:パケットロス - 誰に:同じ家 / いつ:ときどきランダムに - 主担当:外部・外部 - 外部の対応:ユーザーに、ほかのゲームやビデオ通話も途切れるかを確認し、途切れるなら通信事業者に点検を依頼するよう案内。 - グラフでは:不定期なスパイク(パケットロス率、回線の再接続履歴) - 確認箇所:pathping(またはmtr)で通信事業者の最初の区間までのパケットロスを数分間測定し、ルーターの管理画面にあるインターネット(WAN)接続の履歴で再接続の時刻を確認 - 該当する場合:回線が空いているときでも通信事業者の最初の区間から継続的にパケットロスが出て、ルーターの履歴にある回線の再接続時刻がフリーズ・切断の時刻と重なる。ほかのゲームやビデオ通話も一緒に途切れる - 該当しない場合:パケットロスがルーターまでの無線区間から始まっていれば「Wi-Fiの干渉・電波の弱さ」、通信事業者の遠い区間から始まっていれば通信事業者の経路 - 確認手段:ユーザー側の環境で確認 - 出典: - [RFC 3635: Definitions of Managed Objects for the Ethernet-like Interface Types](https://www.rfc-editor.org/rfc/rfc3635) · IETF · フレームチェック(FCS)を通らなかったフレームはFCSエラー(dot3StatsFCSErrors)として数え、入力エラー(ifInErrors)に合算 - [Understanding and Mitigating Packet Corruption in Data Center Networks (SIGCOMM 2017)](https://www.microsoft.com/en-us/research/wp-content/uploads/2017/06/CorrOpt_SIGCOMM2017.pdf) · ACM · パケット破損の主な原因は不良な光モジュール・損傷した光ファイバー・汚れたコネクタ・不適切な施工、破損によるパケットロス率は使用量に関係なく一定 - [pathping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pathping) · Microsoft · 区間ごとに一定時間pingを送り、ルーター・リンクごとのパケットロス率を計算して、どの区間でパケットロスが起きているかを表示 #### isp-dns · DNSの障害・遅延 · DNS failure / slowness サーバー名をアドレスに変換するDNSが遅かったり失敗したりすると、ログインサーバーやアップデートサーバーを見つけられません。 - なぜ → すると → 画面では:通信事業者のDNSの障害または設定ミス → ログイン・アップデートサーバーのアドレスが見つからない → 接続ボタンを押した後に長く待たされるか接続不可。すでに接続している人は問題ない - 症状:接続不可・無限ロード / 要因:遅延, パケットロス - 誰に:特定の地域・ISP, 自分だけ / いつ:接続直後・メンテ明け - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:アドレスのキャッシュ(最後に接続に成功したサーバーのアドレスを記憶)、複数のDNSを用意(1つが失敗したら別のDNSに再度問い合わせ)。 - 外部の対応:ユーザーに、DNSをパブリックDNSなど別のものに変えてみるよう案内。 - グラフでは:一部だけ高い(ログイン失敗数(通信事業者別)、DNSの問い合わせ時間) - 確認箇所:Resolve-DnsName -Server(またはnslookup)で、ログインサーバーの名前を通信事業者のDNSとパブリックDNSにそれぞれ問い合わせ、応答時間と結果を比較 - 該当する場合:通信事業者のDNSだけで応答がないか時間がかかり、パブリックDNSに変えるとすぐに接続できる。すでに接続しているユーザーは問題ない - 該当しない場合:どのDNSでもアドレスはすぐに返るのに接続できなければ、経路・ファイアウォール・サーバー側 - 確認手段:ユーザー側の環境で確認 - 実際の事例:meta-2021, cloudflare-dns-2025, aws-2025 - 出典: - [Cloudflare 1.1.1.1 Incident on July 14, 2025](https://blog.cloudflare.com/cloudflare-1-1-1-1-incident-on-july-14-2025/) · Cloudflare · パブリックDNSリゾルバーが62分間停止し、名前を解決できなくなったユーザーは事実上すべてのインターネットサービスが使えなかった - [RFC 8767: Serving Stale Data to Improve DNS Resiliency](https://www.rfc-editor.org/rfc/rfc8767) · IETF · 権威サーバーに届かないときに期限切れのキャッシュを使い続け、障害をしのぐ方式(serve-stale) - [Resolve-DnsName](https://learn.microsoft.com/en-us/powershell/module/dnsclient/resolve-dnsname) · Microsoft · -Serverで問い合わせ先のDNSサーバーを指定して名前を解決 - [nslookup](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/nslookup) · Microsoft · DNSサーバーに直接名前を問い合わせるコマンド #### isp-ddos-path · DDoSによる共有回線の飽和 · DDoS saturating shared links ゲーム会社や同じネットワーク内の別の宛先を狙った大量の攻撃が、共有回線を埋め尽くします。 - なぜ → すると → 画面では:大量の攻撃トラフィックが発生 → 同じ回線を使う正常なトラフィックまで押し出されて破棄される → 多くの人が同時にワープ・切断・接続不可 - 症状:ワープ, 切断, 接続不可・無限ロード / 要因:パケットロス, 遅延 - 誰に:サーバー全体, 特定の地域・ISP / いつ:ときどきランダムに, 人が集中したとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:外部・外部 - インフラチームの対応:DDoS対策サービス、攻撃時のトラフィック迂回、サーバーのアドレスを隠す(防御機器の後ろに置き、実際のアドレスを公開しない)。 - 外部の対応:同じネットワーク内の別の宛先への攻撃なら、通信事業者に上流区間での遮断を依頼。 - グラフでは:上限で頭打ち(回線の受信量(bps・pps)、インターフェースのドロップ) - 確認箇所:自社の回線・機器のインターフェースの受信量と破棄したパケット数、DDoS対策サービスの攻撃検知履歴を、切断が集中した時刻と合わせて確認 - 該当する場合:回線の受信量が回線容量に張り付いて平らになり、ドロップが増え、同じ時刻に複数の地域・通信事業者のユーザーが一斉にワープ・切断 - 該当しない場合:回線に余裕があるのに一部の通信事業者だけ悪ければ、通信事業者区間の混雑・経路の問題 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Infrastructure layer attacks](https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/infrastructure-layer-attacks.html) · AWS · UDPリフレクション・SYNフラッドのような大量の攻撃は、ネットワーク容量をあふれさせたり、ファイアウォール・ロードバランサーのリソースを占有したりする - [Obfuscating AWS resources (BP1, BP4, BP5)](https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/obfuscating-aws-resources-bp1-bp4-bp5.html) · AWS · オリジンサーバーの前にCloudFront・ロードバランサーなどのエッジサービスを置き、直接の露出を減らす - [RFC 7999: BLACKHOLE Community](https://www.rfc-editor.org/rfc/rfc7999) · IETF · 特定のアドレス宛てのトラフィックを破棄するよう、隣接する通信事業者にBGPで通知するBLACKHOLEコミュニティ #### isp-cgnat · 通信事業者の共有IP(CGNAT) · Carrier-grade NAT モバイル回線や一部の通信事業者では、複数の契約者が1つのIPを共有し、アイドル接続のマッピングを短時間で削除します。 - なぜ → すると → 画面では:通信事業者の機器が膨大な数の契約者のセッションテーブルを管理 → セッションテーブルの上限、短いアイドルタイムアウト → しばらく放置した後に切断、同じIPを使う人たちがまとめてブロックされる誤検知 - 症状:切断, 接続不可・無限ロード / 要因:パケットロス - 誰に:特定の地域・ISP / いつ:しばらく放置した後 - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発, インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:クライアント:最も短いアイドルタイムアウト(モバイル回線では30秒前後の場合もある)の半分以下の間隔でハートビートを送る(通信事業者のCGNATのマッピングは内側から出ていくパケットでしか確実に更新されないため、クライアントから送る)、切れたら自動で再接続。サーバー:ハートビートに応答し、一定時間受け取れなければ先に接続を整理、マッピングが変わってアドレス・ポートが変わってもセッショントークンで同じプレイヤーとして引き継ぐ、1つのIPを複数の人が共有していることがあるのでIP単位のブロックポリシーは慎重に(アカウント・端末単位と合わせて判断)。 - インフラチームの対応:ファイアウォール・DDoS対策機器のIPあたりの接続数・毎秒の新規接続数の制限を、通信事業者の共有IPに合わせて調整(モバイル通信事業者のアドレス帯は基準を上げるか例外にする)。 - 数値の目安:モバイル回線のUDPのアイドルタイムアウトは、30秒前後の場合もあります。 - グラフでは:接続が一斉に切れる(切断数(ハートビートのタイムアウト)、切断前のアイドル時間(通信事業者別)) - 確認箇所:接続ログで1つのIPに同時に接続しているアカウント数と通信事業者(ASN)を確認し、アイドル後に切れた接続のアイドル時間を通信事業者別に集計。ユーザー側ではルーターの管理画面でインターネット(WAN)のアドレスを確認 - 該当する場合:モバイル通信事業者のアドレス帯で1つのIPに複数のアカウントが接続し、切断前のアイドル時間が30〜60秒前後の短い値に集中。ルーターのWANアドレスが100.64.0.0/10(通信事業者のNAT用の共有アドレス)か、サーバーから見えるアドレスと異なる - 該当しない場合:通信事業者に関係なく家庭用ルーターのユーザーに集中していれば「NATマッピングの期限切れ」 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [A Multi-perspective Analysis of Carrier-Grade NAT Deployment (IMC 2016)](https://www.icir.org/vern/papers/cgn-imc16.pdf) · ACM · 測定したNATのUDPマッピング保持時間は10〜200秒、74%が1分以下、CGNの中央値はモバイル回線で65秒・固定回線で35秒 - [RFC 6888: Common Requirements for Carrier-Grade NATs (CGNs)](https://www.rfc-editor.org/rfc/rfc6888) · IETF · CGNは契約者あたりの外部ポート数の制限と、新規マッピングの作成速度の制限をサポートしなければならない - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · アドレスを複数人で共有すると、IP単位のブロック(penalty box)が同じアドレスの別の契約者までブロックする - [RFC 6598: IANA-Reserved IPv4 Prefix for Shared Address Space](https://www.rfc-editor.org/rfc/rfc6598) · IETF · 100.64.0.0/10は、通信事業者のNAT(CGN)機器と契約者のルーターの間で使う共有アドレス帯 #### isp-vpn · VPN・ラグ軽減ツール経由 · VPN / game accelerator detour VPNやラグ軽減ツールを使うと、パケットはその会社の中継サーバーを経由します。中継サーバーが遠かったり混雑していたりすると、かえって遅くなります。 - なぜ → すると → 画面では:VPN・ラグ軽減ツールが、ゲームのパケットをすべて中継サーバーに回す → 中継サーバーまでの距離と混雑が加わり、トンネルのヘッダーのせいでMTU(一度に送れるパケットサイズ)も小さくなる → Pingの上昇とパケットロス、同じ中継アドレスを使う人と一緒にブロックされて接続不可 - 症状:入力遅延, ワープ, 接続不可・無限ロード / 要因:遅延, パケットロス - 誰に:自分だけ / いつ:常に, 接続直後・メンテ明け - 主担当:外部・外部 / 副担当:インフラチーム・ネットワークインフラ, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:UDPパケットは1,200バイト以下に保つ(トンネルのヘッダーでMTUが小さくなってもフラグメント化しないように)、IP単位のブロックは、VPN・ラグ軽減ツールの共用中継アドレスを考慮してアカウント・端末単位と合わせて判断。 - インフラチームの対応:海外の利用者が多ければ近くに接続拠点を自前で置く、「ラグ軽減ツールを使ったら良くなった」という報告が集中する通信事業者は経路を点検。 - 外部の対応:ユーザーに、VPN・ラグ軽減ツールをオフにして比較してみるよう案内。 - 数値の目安:近い中継サーバーなら数ms、別の国を回ると数十〜100ms以上が加わります。 - グラフでは:一部だけ高い(RTT(ユーザー別)、接続元IPの事業者) - 確認箇所:接続元IPのASNがVPN・ラグ軽減ツール・ホスティング事業者かを確認し、ユーザーにVPN・ラグ軽減ツールをオフにしてPingとtracerouteを比較してもらう - 該当する場合:VPN・ラグ軽減ツールを使ったときだけRTT・パケットロスが増えるか接続がブロックされ、tracerouteに中継サーバーを経由する区間が見える - 該当しない場合:オンでもオフでも同じなら回線・通信事業者の区間。オンにした方が良くなるなら、元の通信事業者の経路(「迂回ルーティング」「ピーク時間帯のピアリング混雑」)の問題 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:逆に、通信事業者の経路が悪いときは、ラグ軽減ツールがより良い経路を通ってPingが下がることもあります。「ラグ軽減ツールを使ったら良くなった」という報告は、迂回ルーティングや夜の混雑といった通信事業者の経路の問題の手がかりです。 - 出典: - [RFC 4459: MTU and Fragmentation Issues with In-the-Network Tunneling](https://www.rfc-editor.org/rfc/rfc4459) · IETF · ネットワーク途中のトンネルのカプセル化ヘッダーで送れるサイズが小さくなって起きる、フラグメンテーション・経路MTUの問題 - [RFC 8899: Packetization Layer Path MTU Discovery for Datagram Transports](https://www.rfc-editor.org/rfc/rfc8899) · IETF · UDPなどのデータグラム通信の基本となる安全なサイズ(BASE_PLPMTU)として1,200バイトを推奨 - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · 別の国の拠点を経由するときに加わる往復遅延の規模:ソウル・東京間30ms、ソウル・香港間39ms、ソウル・シンガポール間68ms ### L5 データセンターのネットワーク機器(原因11件) #### dc-firewall · ファイアウォールのセッションテーブル飽和 · Firewall session table exhaustion ファイアウォールは、通過させたすべての接続をセッションテーブルに記録して追跡します。テーブルがいっぱいになると、新しい接続を受け付けられなくなります。 - なぜ → すると → 画面では:接続の殺到や攻撃でセッション数が上限に到達 → 新しい接続を記録する空きエントリがなく拒否 → 新たに入ろうとする人は接続不可・無限ロード、一部の既存の接続も切断 - 症状:接続不可・無限ロード, 切断 / 要因:パケットロス - 誰に:サーバー全体 / いつ:接続直後・メンテ明け, 人が集中したとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:ログイン待機列システムで一斉に押し寄せる接続を調整、短い接続を繰り返さないよう接続を再利用、ハートビートが途切れた接続は先に整理(切れた接続がセッションテーブルを長く占有しないように)。クライアント:最も短いアイドルタイムアウトの半分以下の間隔でハートビートを送る、切れたら自動で再接続し、再試行間隔を延ばしながらランダムに分散(一斉に再び殺到しないように)。 - インフラチームの対応:セッションテーブルのサイズを増やす、短時間で終わった接続を早く整理(終了したセッションのタイムアウトを短縮)、アイドルセッションのタイムアウトを短くするときはその値をゲーム開発チームに伝えてハートビート間隔を合わせる、攻撃の遮断、セッション数の使用率にアラート。 - グラフでは:上限で頭打ち(ファイアウォールのセッション数、新規接続の失敗数) - 確認箇所:ファイアウォール機器の同時セッション数をセッション上限と一緒にグラフで確認し、機器のログでセッションを作れずに破棄した記録を探す。Linuxのファイアウォールならnf_conntrack_countをnf_conntrack_maxと比較してdmesgの「nf_conntrack: table full, dropping packet」を、AWSのインスタンスならethtool -Sのconntrack_allowance_exceededを確認 - 該当する場合:セッション数が上限で平らになった時刻から新規接続の失敗が増え、セッション作成失敗の記録や破棄カウンターも一緒に増える - 該当しない場合:セッション数が上限にほど遠いのに接続できなければ「接続待ちキュー(backlog)のあふれ」かログインサーバー側。アイドル接続だけが切れるなら「クラウドのセキュリティグループによる接続追跡の期限切れ」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · 接続追跡テーブルの最大エントリ数(nf_conntrack_max)、終了処理中の接続の保持時間(TIME_WAIT・FIN_WAITはデフォルト120秒)、確立済みTCPはデフォルト5日、現在のエントリ数(nf_conntrack_count) - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · インスタンスごとに追跡できる接続数を超えると新しい接続のパケットを破棄、アイドル接続が追跡テーブルを枯渇させることがある - [Infrastructure layer attacks](https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/infrastructure-layer-attacks.html) · AWS · SYNフラッドのような攻撃は、サーバー・ファイアウォール・ロードバランサーのリソースを占有する - [net/netfilter/nf_conntrack_core.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_core.c?h=v6.12) · Linux kernel · 接続追跡テーブルがいっぱいになると「nf_conntrack: table full, dropping packet」を記録し、新しい接続のパケットを破棄 - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · conntrack_allowance_exceeded:インスタンスの接続追跡の上限を超えて破棄されたパケット数、ethtool -Sで確認 #### dc-ddos · DDoS対策の経由・誤検知 · DDoS scrubbing latency, false positives 攻撃を防ぐためにトラフィックをスクラビングセンターに回すと経路が長くなり、正常なユーザーを攻撃と誤認してブロックすることもあります。 - なぜ → すると → 画面では:攻撃の検知後(または常時)、入ってくるトラフィックをスクラビングセンターに迂回 → 経路が長くなり、一部の正常なパケットを攻撃と判定 → 全体のPingが上昇、特定の地域・通信事業者だけ接続不可 - 症状:入力遅延, 接続不可・無限ロード, ワープ / 要因:遅延, パケットロス - 誰に:サーバー全体, 特定の地域・ISP / いつ:人が集中したとき, ときどきランダムに - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ゲームのトラフィックパターン(ポート、パケットサイズ、毎秒のパケット数)をまとめてインフラチームに共有、UDPパケットは1,200バイト以下に保つ。 - インフラチームの対応:ゲームのトラフィックパターンに合わせた防御ルール、地域ごとのスクラビング拠点、トンネル区間でTCPのパケットサイズを小さくする(MSSの調整)、地域・通信事業者別の接続失敗率で誤検知を確認。 - 数値の目安:スクラビング拠点が同じ国にあれば数ms、別の国の拠点を経由すると30〜100ms以上が加わります。通常は入ってくる方向だけが迂回し、サーバーの応答は直接出ていきます。フィルタリング済みのトラフィックをトンネルで戻して受け取る場合は、一度に送れるサイズ(MTU)も小さくなり、大きなパケットだけが消える問題につながることもあります。 - グラフでは:ある時点から階段状に上昇(RTT(Ping)、地域・通信事業者別の接続失敗率) - 確認箇所:防御機器・サービスの迂回(スクラビング)の開始・終了記録と遮断ログを、RTTのグラフや地域・通信事業者別の接続失敗率と同じ時間軸に並べて確認。問題の地域からmtr・tracerouteで、経路にスクラビング拠点が入っていないか確認 - 該当する場合:迂回がオンになった時刻にRTTが一段上がってとどまり、オフになると戻る。または遮断ログに正常なユーザーのアドレスがあり、その地域・通信事業者だけ接続失敗率が上がる - 該当しない場合:迂回・遮断の記録がない時刻にRTTが上がるなら「迂回ルーティング」か「BGPの経路変更・収束」。大きなパケットだけが消えるなら「MTUの不一致(大きなパケットだけ消える)」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Maximum transmission unit and maximum segment size](https://developers.cloudflare.com/magic-transit/reference/mtu-mss/) · Cloudflare · 入ってくるトラフィックはフィルタリング後にGREトンネル(MTU 1,476)で転送、出ていく応答はインターネットへ直接(DSR)、TCP MSSは1,436以下に制限することを推奨、合わせないと大きなパケットが破棄されるかフラグメント化される - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · 拠点の位置ごとの往復遅延の規模:ソウル・釜山地域間8ms、ソウル・東京間30ms、ソウル・シンガポール間68ms #### dc-lb-idle · ロードバランサーのアイドルタイムアウト · Load balancer idle timeout ロードバランサーは、アイドル状態の接続を一定時間後に削除します。ゲーム側は接続が維持されているものとみなしているうちに、切断されてしまいます。 - なぜ → すると → 画面では:プレイヤーがしばらくパケットを何も送らない(会話ウィンドウ、離席) → ロードバランサーがアイドル接続を整理(よくあるデフォルト値は60〜350秒) → 再び動いた瞬間に切断 - 症状:切断 / 要因:パケットロス - 誰に:自分だけ, サーバー全体 / いつ:しばらく放置した後 - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・クライアント開発, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:最も短いアイドルタイムアウトの半分以下の間隔でハートビートを送る(60秒のALBの後ろなら30秒以下)、切れたら自動で再接続。サーバー:ハートビートに応答し、一定時間受け取れなければ先に接続を整理、セッショントークンで引き継ぐ。 - インフラチームの対応:経路上にあるロードバランサーのアイドルタイムアウトの値を確認してゲーム開発チームに共有し、必要なら延ばす。 - 数値の目安:AWS ALBは60秒、NLBはTCP 350秒・UDP 120秒、Azure Load BalancerはTCP 4分がデフォルト値です。ALBとNLBのTCPの値は変更できますが、NLBのUDP 120秒は変更できません。ALBは時間が来るとサーバー側の接続も閉じますが、NLBは黙って削除するため、サーバーが気づかないまま接続が残りやすくなります。 - グラフでは:接続が一斉に切れる(切断数、切断前のアイドル時間) - 確認箇所:経路上にあるロードバランサーのアイドルタイムアウトの設定値を確認し、切れた接続ごとに最後のパケットから切断までにかかった時間を集計。AWS NLBならCloudWatchのTCP_ELB_Reset_Count(ロードバランサーが送ったRSTの数)も確認 - 該当する場合:切れた接続のアイドル時間が設定値(ALB 60秒、NLB TCP 350秒など)の直後に集中し、その時間より長く放置してから動くと再現する。NLBではその時刻にTCP_ELB_Reset_Countが増える - 該当しない場合:アイドル時間と関係なく切れるならこの原因ではない。ロードバランサーを通さずに直接つなぐサーバーで350秒付近に集中するなら「クラウドのセキュリティグループによる接続追跡の期限切れ」、ユーザーの家庭用ルーター側なら「NATマッピングの期限切れ」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Edit attributes for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-load-balancer-attributes.html) · AWS · ALBのアイドルタイムアウトはデフォルト60秒(1〜4,000秒)、クライアント・ターゲットとの接続がこの時間通信しなければロードバランサーが接続を閉じる - [Network Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html) · AWS · NLBのTCPアイドルはデフォルト350秒(60〜6,000秒)、過ぎると追跡だけを止め、その後にデータが来るとRST、UDPフローの120秒は変更不可 - [Configure load balancer TCP reset and idle timeout](https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-tcp-idle-timeout) · Microsoft Azure · Azure Load Balancerのアイドルタイムアウトはデフォルト4分(4〜100分)、超えるとセッション維持の保証なし、TCPリセットはオプション設定 - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · TCP_ELB_Reset_Count:ロードバランサーが生成して送ったRSTパケットの数 #### dc-cloud-conntrack · クラウドのセキュリティグループによる接続追跡の期限切れ · Cloud security group connection tracking timeout クラウドのサーバーに付いているファイアウォール(セキュリティグループ)も接続を追跡し、アイドル接続の追跡エントリは決められた時間の後に期限切れになります。ロードバランサーを通さずに直接つなぐサーバーでも、しばらく放置していたプレイヤーが切断されることがあります。 - なぜ → すると → 画面では:セキュリティグループがゲームの接続を追跡する設定(特定のアドレスだけ許可、アウトバウンドルールの制限、NLB経由など) → しばらくアイドル状態だった接続の追跡エントリが期限切れになり、その後に届いたパケットをセキュリティグループが黙って破棄 → 離席後に再び動くと反応がないまま切断。サーバープログラムはしばらく気づかない - 症状:切断 / 要因:パケットロス - 誰に:自分だけ, サーバー全体 / いつ:しばらく放置した後 - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・クライアント開発, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:最も短いアイドルタイムアウトの半分以下の間隔でハートビートを送る(TCP 350秒なら175秒以下、UDPストリーム180秒なら90秒以下)、切れたら自動で再接続。サーバー:ハートビートに応答し、一定時間受け取れなければ先に接続を整理、セッショントークンで引き継ぐ。 - インフラチームの対応:インスタンスの接続追跡時間(TcpEstablishedTimeout)を確認し必要なら延ばす(UDPは180秒が最大のため延ばせない)、追跡が発生しないセキュリティグループ構成を検討(ゲームのポートはすべてのアドレスを許可、アウトバウンドルールはすべて許可。NLBを経由する接続はそれでも追跡される)、新世代のインスタンスに移行するときにアイドル試験。 - 数値の目安:AWSでは、Nitro v6のインスタンスタイプはアイドル状態のTCP接続の追跡エントリをデフォルトで350秒後に削除します(それ以外のタイプは5日)。UDPは、要求と応答が何度もやり取りされたフロー(ストリーム)が180秒、一方向だけに流れたか要求・応答が1回だけのフローが30秒がデフォルトです。 - グラフでは:接続が一斉に切れる(切断数、切断前のアイドル時間) - 確認箇所:インスタンスの接続追跡時間の設定とセキュリティグループのルール(追跡が発生する構成か)を確認し、切れた接続のアイドル時間を集計。切れた直後にサーバーでss -tnoiを実行し、その接続がESTABLISHEDのまま残って再送タイマー(timer:(on,…))が動き、backoffが増えていくかを確認 - 該当する場合:切れた接続のアイドル時間がTCP 350秒、UDPストリーム180秒、UDP一方向30秒の直後に集中し、サーバー側のソケットは切断を検知できないままESTABLISHEDで残る(サーバーに送るデータがあれば再送だけを繰り返す) - 該当しない場合:セキュリティグループが追跡しない構成(ゲームのポートをすべてのアドレスに許可、アウトバウンドルールはすべて許可、NLBを経由しない)ならこの原因ではない。NLBを経由するなら「ロードバランサーのアイドルタイムアウト」と値を比較 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · TCPアイドルの追跡はデフォルト350秒(Nitro v6、それ以外は432,000秒=5日)、UDPは一方向30秒・ストリーム180秒(最大180)、すべてのアドレスを許可するルールなら追跡しない、NLB経由の接続は常に追跡 - [Update the TCP idle timeout for your Network Load Balancer listener](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/update-idle-timeout.html) · AWS · NLBのアイドルタイムアウトがターゲットインスタンスの接続追跡時間より長いと、インスタンス側が先に黙って接続状態を破棄する - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · -oのtimer:(on,…)は再送タイマー、-iのbackoffは再送の待ち時間を2倍ずつ延ばした回数 #### dc-nat-gateway · クラウドのNATゲートウェイの接続・ポート上限 · Cloud NAT gateway connection / port limits プライベートサブネットのサーバーから外部(プラットフォーム認証・決済・外部API)への接続は、NATゲートウェイがアドレスとポートを変換して送り出します。同じ宛先への同時接続がゲートウェイのポート上限を超えると、新しい接続が失敗します。 - なぜ → すると → 画面では:サーバー群が、プラットフォーム認証・決済のような同じ外部アドレスへ短い接続を大量に開くか、接続を長く開いたままにする → NATゲートウェイがその宛先に使う送信元ポートをこれ以上割り当てられず、新しい接続が失敗 → ゲーム内は問題ないのに、ログイン・決済・報酬付与のように外部を呼び出す機能だけが失敗するか遅れる(接続不可・無限ロード、不発・ロールバック) - 症状:接続不可・無限ロード, 不発・ロールバック / 要因:パケットロス, 遅延 - 誰に:特定の機能だけ, サーバー全体 / いつ:接続直後・メンテ明け, 夜のピーク時間帯, 人が集中したとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:外部APIは接続を再利用し(HTTP keep-alive、コネクションプール)、リクエストごとに新しい接続を開かない、プールに残したアイドル接続はNATのアイドルタイムアウト(AWSは350秒)より短い間隔でkeepaliveを送るか先に閉じる、失敗したら再試行間隔を延ばしながらランダムに分散、外部呼び出しごとの失敗率・遅延を記録。 - インフラチームの対応:NATゲートウェイにIPアドレスを追加(AWSのパブリックNATゲートウェイにはElastic IPをデフォルトで2つまでしか付けられないため、それ以上はクォータの引き上げを申請)、アベイラビリティーゾーン・サブネットごとにゲートウェイを分ける、ポート割り当て失敗のメトリクス(AWSのErrorPortAllocation、AzureのSNAT Connection CountのFailed、Google Cloudのdropped_sent_packets_countのOUT_OF_RESOURCES)にアラート、Google Cloud NATはVMあたりの最小ポート数を増やすか動的ポート割り当てを使う。 - 数値の目安:AWS NATゲートウェイは、IPアドレス1つで同じ宛先(IP・ポート・プロトコル)に同時接続を55,000まで開くことができ、IPを8つまで付けて増やせます。350秒間通信のない接続は削除し、その後この接続に送られたパケットにはRSTを返します。Azure NAT Gatewayは、パブリックIP 1つあたりSNATポート64,512個(IPは最大16個)です。Google Cloud NATはNAT IP 1つあたり64,512ポートをVMごとに分けて割り当てますが、VMあたりの最小ポート数のデフォルトが64(静的割り当て)のため、デフォルト設定では1台のVMが同じ宛先に同時に開ける接続は、たいてい64に制限されます。 - グラフでは:上限で頭打ち(NATゲートウェイの同時接続数、ポート割り当ての失敗数) - 確認箇所:AWSはCloudWatchのNATゲートウェイのメトリクスErrorPortAllocation・ActiveConnectionCount・PacketsDropCount(AzureはSNAT Connection CountをFailed状態で絞り込んだ値とDropped Packets、Google Cloudはdropped_sent_packets_countのreason OUT_OF_RESOURCES)を、ゲームサーバーの外部呼び出しが失敗した時刻と並べて確認 - 該当する場合:外部呼び出しが失敗した時刻にErrorPortAllocation(AzureはFailed状態のSNAT Connection Count、Google CloudはOUT_OF_RESOURCESによる破棄)が0より大きくなり、失敗が認証・決済サーバーのように接続が多く集中する1〜2か所の宛先への呼び出しに集まる - 該当しない場合:ポート割り当ての失敗が0なのに、ゲームサーバーのconnectがEADDRNOTAVAILで失敗し、TIME_WAITがエフェメラルポートの範囲に迫っていれば「サーバー間接続のエフェメラルポート枯渇」。接続はできるのに応答だけが遅ければ「外部サービスへの依存」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:サーバー1台のエフェメラルポートが尽きる「サーバー間接続のエフェメラルポート枯渇」とは異なり、この上限はNATゲートウェイにかかり、ゲートウェイの後ろのサーバー群が共有します(Google Cloud NATはVMごとに分けて割り当て)。サーバー側のTIME_WAITとエフェメラルポートの範囲には余裕があるのに外部呼び出しだけが失敗するなら、こちらです。閉じた接続のポートもすぐには同じ宛先に再利用されないため(Azureはクールダウン、Google CloudはTIME_WAITの間は使用不可)、短い接続を繰り返すほど早く上限に達します。 - 出典: - [NAT gateway basics](https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-basics.html) · AWS · IPv4アドレス1つあたり同じ宛先(宛先IP・ポート・プロトコル)へ同時接続55,000、IPを8つまで付けて拡張(パブリックNATゲートウェイのElastic IPはデフォルト2つ、クォータの引き上げ申請で増やす)、帯域幅は5Gbpsから100Gbpsまで、処理量は毎秒100万から1,000万パケットまで自動で増え、その上限を超えるとパケットを破棄 - [NAT gateway metrics and dimensions](https://docs.aws.amazon.com/vpc/latest/userguide/metrics-dimensions-nat-gateway.html) · AWS · ErrorPortAllocation:送信元ポートを割り当てられなかった回数(0より大きければ同時接続が多すぎる)、ActiveConnectionCount、IdleTimeoutCount(350秒のアイドルで整理された接続)、PacketsDropCount - [Troubleshoot NAT gateways](https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-troubleshooting.html) · AWS · 350秒アイドルだと接続が期限切れになり、続けて送るとRSTを返す、350秒より短いkeepaliveを推奨、接続上限に達したらアベイラビリティーゾーンごとのゲートウェイ・IPの追加・接続数の削減 - [Source Network Address Translation (SNAT) with Azure NAT Gateway](https://learn.microsoft.com/en-us/azure/nat-gateway/nat-gateway-snat) · Microsoft Azure · パブリックIP 1つあたりSNATポート64,512個(IPは最大16個)、同じ宛先への接続ごとに別のポートが必要、閉じたポートは同じ宛先に再利用する前にクールダウン - [Metrics and alerts for Azure NAT Gateway](https://learn.microsoft.com/en-us/azure/nat-gateway/nat-metrics) · Microsoft Azure · SNAT Connection CountをFailed状態で絞り込んだ値が0より大きければSNATポート枯渇の可能性、Dropped Packets - [IP addresses and ports](https://cloud.google.com/nat/docs/ports-and-addresses) · Google Cloud · NAT IP 1つあたりTCP・UDPそれぞれ64,512ポート、VMあたりの最小ポートのデフォルトは64(静的割り当て)・32(動的割り当て)、VMに予約されたポート数が同じ宛先への同時接続数を制限、閉じた接続はTIME_WAITの間使えない - [Logs and metrics](https://cloud.google.com/nat/docs/monitoring) · Google Cloud · dropped_sent_packets_countのreason OUT_OF_RESOURCES:NAT IPやポートが足りずに破棄したパケット #### dc-lb-imbalance · ロードバランサーの偏り・ヘルスチェックの誤判定 · LB imbalance, bad health checks 接続が1台のサーバーだけに集中したり、すでに落ちたサーバーに人を送り続けたりします。 - なぜ → すると → 画面では:振り分けルールが合っていないか、ヘルスチェックが実際の状態を捉えられていない → 1台のサーバーだけが過負荷、または落ちたサーバーへの接続試行 → 一部のチャンネル・一部の人だけスローモーション、接続不可・無限ロード - 症状:スローモーション, 接続不可・無限ロード / 要因:ストール, パケットロス - 誰に:特定の場所・チャンネル / いつ:接続直後・メンテ明け, 人が集中したとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ロードバランサーからの確認リクエストに、実際のゲームの状態(ティックの進行、DB接続)を見て応答するヘルスチェックの実装、サーバーの負荷の値も一緒に返す。 - インフラチームの対応:ヘルスチェックを実際のゲームの応答を確認する方式に変える、サーバー負荷に基づく振り分け、サーバーごとの接続数の差を監視。 - グラフでは:一部だけ高い(サーバーごとの接続数・CPU使用率) - 確認箇所:ロードバランサー配下のサーバーごとの接続数(ss -s)とCPU使用率を1つのグラフに重ねて確認し、ロードバランサーのターゲットのヘルス状態(AWSはCloudWatchのHealthyHostCount・UnHealthyHostCount)をゲームサーバーの実際の状態と比較 - 該当する場合:1〜2台のサーバーだけ接続数・CPUがほかのサーバーより大きく高いか、ティックが止まったサーバーがヘルス状態「正常」のまま新しい接続を受け続けている - 該当しない場合:サーバーごとの接続数が均等なのに1つのチャンネルだけ重ければ、そのチャンネル内の負荷(「シングルスレッドのエリア過負荷(ホットスポット)」) - 確認手段:インフラのツールで確認(ゲームコード不要) - 実際の事例:aws-2025 - 出典: - [Load Balancing in the Datacenter](https://sre.google/sre-book/load-balancing-datacenter/) · Google · 単純なラウンドロビンではタスク間のCPU使用量が最大2倍まで開く、バックエンドが応答・ヘルスチェックに負荷を載せて返す重み付き振り分け、リクエストの受け付けを止めるlame duck状態 - [Health checks for Network Load Balancer target groups](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/target-group-health-checks.html) · AWS · ヘルスチェックはデフォルト30秒間隔・2回失敗で除外、UDPサービスはTCP・HTTPのヘルスチェックで確認するため、実際のサービス状態を反映するよう構成することを推奨 - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · HealthyHostCount・UnHealthyHostCount:正常・異常と判定されたターゲットの数 #### dc-microburst · スイッチのマイクロバースト · Switch microburst drops 複数のサーバーが同じ瞬間に数千人へ一斉にパケットを送ると、そのトラフィックが集まるスイッチポートの小さなバッファが1ms足らずであふれます。 - なぜ → すると → 画面では:ワールドボスの出現・大規模スキル、または複数サーバーのティックが同じ瞬間に重なって一斉に送信 → 複数のポートが1つのポートに集まる場所や、速いポートから遅いポートへ渡る場所のバッファ(ポートあたり数百KB〜数MB)が瞬間的にいっぱいになる → 一部のパケットが破棄され、多くの人が同時にワープ・スキル不発 - 症状:ワープ, 不発・ロールバック / 要因:パケットロス - 誰に:特定の場所・チャンネル / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・ネットワークインフラ, インフラチーム・サーバーインフラ - ゲーム開発チームの対応:送信をティック内で均等に分ける(ペーシング)、サーバーごとにティックの開始タイミングを少しずつずらす。 - インフラチームの対応:ネットワーク:バッファの大きなスイッチ、トラフィックの分散(サーバーを複数のスイッチ・ポートに分けて配置)、スイッチポートごとの破棄カウンターを監視。サーバー機器・OS:サーバー全体の送信速度の上限(Linuxのtcシェーパー)。 - 数値の目安:10Gbpsのポートが1msで送り出せる量は約1.25MBです。2つのポートのトラフィックが1つのポートに同時に集まると、1msごとに1.25MBずつたまります。1秒平均の使用率が10%でも、1ms単位ではあふれることがあります。 - グラフでは:人数・負荷に連動して上昇(スイッチポートの出力破棄数) - 確認箇所:サーバーがつながるスイッチポートと、そのトラフィックが集まるポートの出力破棄カウンター(ifOutDiscards、機器によってはoutput drops)をできるだけ短い間隔で集め、ボスの出現・大規模戦闘の時刻と照合。1秒・1分平均の使用率グラフだけでは見えない - 該当する場合:平均使用率は低いのに、人が1か所に集まる瞬間ごとに出力破棄が増え、そのときに複数のユーザーが同時にワープ・スキル不発を報告する - 該当しない場合:平均使用率が高い時間帯に破棄が継続的に増えるなら「データセンター回線の飽和」。入力エラー(CRC)が増えるなら「ケーブル不良・ポートエラー」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [High-Resolution Measurement of Data Center Microbursts (IMC 2017)](https://conferences.sigcomm.org/imc/2017/papers/imc17-final60.pdf) · ACM · データセンターのバーストの70%以上は数十µs以内に終わる、平均使用率が約9%のポートでもバーストでパケットが破棄される - [Data Center TCP (DCTCP) (SIGCOMM 2010)](https://conferences.sigcomm.org/sigcomm/2010/papers/sigcomm/p63.pdf) · ACM · 汎用スイッチはバッファが浅い(48ポートで4MBを共有、1ポートが使えるのは約700KBまで)、複数のフローが短い瞬間に1つのポートに集まるとパケットロス - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifOutDiscards:エラーはないのに、バッファ領域の確保などの理由で送り出せずに破棄したパケット数 #### dc-uplink · データセンター回線の飽和 · Uplink saturation アップデートデータの配信・ログ転送・バックアップがゲームと同じ回線を使うと、回線がいっぱいになります。 - なぜ → すると → 画面では:大容量の転送が同じ回線を占有 → 回線のキューとパケットロスが増加 → サーバー全体でPingの上昇とワープ - 症状:入力遅延, ワープ / 要因:遅延, パケットロス - 誰に:サーバー全体 / いつ:一定の周期で, 人が集中したとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ - インフラチームの対応:ネットワーク:ゲームのトラフィックを優先処理(QoS)、大容量転送用の回線を分離、回線使用率のアラート。サーバー機器・OS:バックアップ・ログ転送・デプロイに速度制限をかけ、空いている時間帯に実行。 - グラフでは:上限で頭打ち(回線使用率、RTT(Ping)) - 確認箇所:データセンター回線(アップリンク)のインターフェースの使用率(SNMPのifHCInOctets・ifHCOutOctetsから計算)と出力破棄(ifOutDiscards)を、バックアップ・デプロイ・ログ転送のスケジュールと同じ時間軸に並べて確認 - 該当する場合:回線使用率が帯域幅の上限に張り付いて平らになった時刻に、サーバー全体のRTTと破棄が上がり、その時刻が大容量の転送作業と重なる - 該当しない場合:分単位の使用率が上限にほど遠いのに破棄があるなら「スイッチのマイクロバースト」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 4594: Configuration Guidelines for DiffServ Service Classes](https://www.rfc-editor.org/rfc/rfc4594) · IETF · ゲームのようなリアルタイムの対話型トラフィックと、バックアップのような大容量転送を別々のサービスクラスに分けて処理 - [RFC 7567: IETF Recommendations Regarding Active Queue Management](https://www.rfc-editor.org/rfc/rfc7567) · IETF · 機器に入ってくる量が送り出す速度を超えるとキューがたまり、過剰なキューが遅延の主な原因 - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifHCInOctets・ifHCOutOctets:インターフェースで受信・送信したバイト数(64ビット)、ifOutDiscards:送り出せずに破棄したパケット数 #### dc-failover · ネットワーク機器のフェイルオーバー · Network device failover ルーター・ファイアウォールの1台が故障して予備機に切り替わる(フェイルオーバー)数秒の間、全員が止まります。 - なぜ → すると → 画面では:機器の故障またはメンテナンスで予備機に切り替え → 切り替えに数秒、セッション情報が同期されていなければ接続がリセットされる → サーバーの全ユーザーが同時にフリーズ、大量の切断 - 症状:フリーズ, 切断 / 要因:パケットロス - 誰に:サーバー全体 / いつ:ときどきランダムに - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:短い途切れ(数秒)に耐えるタイムアウト、切れても再接続すればセッショントークンでセッションを引き継げるようにする。クライアント:切れたら自動で再接続(一斉に殺到しないよう再試行間隔をランダムに分散)。 - インフラチームの対応:接続状態を共有する冗長化、BFDで故障を1秒以内に検知、切り替え試験を定期的に実施。 - 数値の目安:機器が故障をすぐに検知すれば1〜3秒前後です。高速な故障検知(BFD)なしでBGPのデフォルトのタイマーだけに任せると、隣接する機器が検知するまでの90〜180秒間、経路が途切れることがあります。 - グラフでは:接続が一斉に切れる(接続数、サーバー全体の送受信量) - 確認箇所:ルーター・ファイアウォールのイベントログ(VRRPの役割変更、BFD・BGPセッションのダウン、フェイルオーバーの記録)と、同じ時刻のサーバー全体の接続数・送受信量を確認 - 該当する場合:機器ログの切り替え時刻に、その機器の配下にあるすべてのサーバーのトラフィックが数秒間0になるか、接続数が同時に落ちる - 該当しない場合:1台のサーバーの接続だけが落ちるなら「サーバークラッシュ」か「NICドライバー・ファームウェアの問題」。機器ログがきれいで、止まったサーバーがクラウドの仮想マシン1台なら「クラウドホストのメンテナンス・ライブマイグレーション」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 5880: Bidirectional Forwarding Detection (BFD)](https://www.rfc-editor.org/rfc/rfc5880) · IETF · ルーティングプロトコルのHello方式は故障の検知に1秒以上かかるため、より短い時間で検知するために作られたBFD - [RFC 7938: Use of BGP for Routing in Large-Scale Data Centers](https://www.rfc-editor.org/rfc/rfc7938) · IETF · BGPのkeepaliveだけに頼ると収束が遅く、リンクダウンを即座に受け取ってセッションを切ればms単位で検知して再収束 - [RFC 5798: Virtual Router Redundancy Protocol (VRRP) Version 3 for IPv4 and IPv6](https://www.rfc-editor.org/rfc/rfc5798) · IETF · VRRPアドバタイズメントはデフォルト1秒間隔、予備機はアドバタイズメントが約3倍の間隔を超えて途切れると役割を引き継ぐ(デフォルト設定で3秒強) #### dc-bad-cable · ケーブル不良・ポートエラー · Bad cable / optics (CRC errors) 光モジュールやケーブルが不良だと、その経路を通るパケットが一定の割合で壊れます。 - なぜ → すると → 画面では:光モジュール・ケーブルの不良でビットエラー → 壊れたパケットは機器が黙って破棄 → その経路を使う一部のサーバー・ユーザーだけが、継続的なパケットロスでワープ・引き戻し - 症状:ワープ, 引き戻し / 要因:パケットロス - 誰に:特定の場所・チャンネル / いつ:常に - 主担当:インフラチーム・ネットワークインフラ - インフラチームの対応:ポートエラー(CRC)カウンターの監視・アラート、光モジュール・ケーブルなどの部品交換、交換までは問題のリンクを外して迂回。 - グラフでは:一部だけ高い(ポートごとのCRCエラー数、サーバー・経路ごとのパケットロス率) - 確認箇所:リンクの両端のCRCカウンターを確認。スイッチはポートのFCSエラー(dot3StatsFCSErrors)・入力エラー(ifInErrors)、サーバーはip -s -s linkのRX errorsのうちcrc(カーネル統計のrx_crc_errors) - 該当する場合:1つのポートのCRCエラーがトラフィック量・時間帯と関係なく継続的に増え、そのポートを通るサーバー・ユーザーだけにパケットロスがある - 該当しない場合:CRCエラーがなく出力破棄だけが増えるなら混雑(「スイッチのマイクロバースト」「データセンター回線の飽和」) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Understanding and Mitigating Packet Corruption in Data Center Networks (SIGCOMM 2017)](https://www.microsoft.com/en-us/research/wp-content/uploads/2017/06/CorrOpt_SIGCOMM2017.pdf) · ACM · データセンターの35万リンクを分析:破損の原因は不良な光モジュール・損傷した光ファイバー・汚れたコネクタ、破損率は使用量に関係なく一定、経路数を維持しながら問題のリンクを外して修理 - [RFC 3635: Definitions of Managed Objects for the Ethernet-like Interface Types](https://www.rfc-editor.org/rfc/rfc3635) · IETF · フレームチェック(FCS)の失敗カウンター(dot3StatsFCSErrors)、このエラーは入力エラー(ifInErrors)に合算 - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · rx_crc_errors:CRCエラーで受信したパケット数、ip -s -s linkでエラーの種類ごとに確認 #### dc-mtu · MTUの不一致(大きなパケットだけ消える) · MTU black hole 途中の区間のMTU(一度に送れるサイズ)が小さくなっているのにサイズ超過の通知がブロックされると、大きなパケットだけが消え続けます。 - なぜ → すると → 画面では:トンネル・VPNの区間でMTUが小さくなる → サイズ超過の通知(ICMP)がファイアウォールでブロックされ、送信側が気づかない → インベントリ・キャラクター一覧のような大きな画面を開いたときだけフリーズした後に切断 - 症状:フリーズ, 切断, 接続不可・無限ロード / 要因:パケットロス - 誰に:特定の地域・ISP, 自分だけ / いつ:特定の操作をしたとき, 接続直後・メンテ明け - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:サーバー側で直接下げるなら、ソケットの最大セグメントサイズ(TCP_MAXSEG)を設定(ゲームのコードでメッセージを細かく分けるだけでは防げない)、UDPパケットは1,200バイト以下に保つ。 - インフラチームの対応:ネットワーク:トンネル区間でTCPのパケットサイズを小さくする(MSSの調整)、ファイアウォール・クラウドのネットワークACLでサイズ超過の通知(ICMP)を許可。サーバー機器・OS:サーバーのファイアウォール・クラウドのセキュリティグループでもサイズ超過の通知(ICMP)を許可、サーバーカーネルのMTU探索(tcp_mtu_probing=1)を有効化(数秒止まった後にようやく働く最後のセーフティネット)。 - 数値の目安:通常は1,500バイトで、トンネルを通ると1,400前後に小さくなります。 - グラフでは:一部だけ高い(地域・通信事業者別の切断、大きな応答の失敗) - 確認箇所:問題のユーザーのPCからサーバーへ、フラグメント禁止の印(DF)を付けたpingをサイズを変えながら送る。Windowsはping /f /l 1472 SERVER_IP、Linuxはping -M do -s 1472 SERVER_IP(1,472はMTU 1,500からIPヘッダー20バイトとICMPヘッダー8バイトを引いた値)。サイズを小さくしながら通る最大サイズを探し、サーバー側のセキュリティグループ・ファイアウォールがICMPのサイズ超過通知(Fragmentation Needed)を許可しているか確認 - 該当する場合:小さいpingは通るのに1,472バイトのDF付きpingは失敗し(応答なし、またはフラグメント化が必要というエラー)、通る最大サイズが1,400前後と小さい。同じ地域のユーザーが大きな画面を開いたときだけフリーズする - 該当しない場合:1,472バイトのDF付きpingも問題なく通るなら経路MTUの問題ではない。小さいpingも通らないならICMP自体がブロックされているため、この方法では判断できない - 確認手段:ユーザー側の環境で確認 - 出典: - [RFC 2923: TCP Problems with Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc2923) · IETF · ファイアウォールがICMP(Fragmentation Needed)をブロックすると経路MTU探索が失敗し、大きなパケットだけが消え続ける(ブラックホール)、pingや小さな通信は通るため診断が難しい - [Maximum transmission unit and maximum segment size](https://developers.cloudflare.com/magic-transit/reference/mtu-mss/) · Cloudflare · インターネットの経路MTUは1,500、GREトンネルを通ると1,476、TCP MSSは1,436以下に制限することを推奨 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_mtu_probing=1は普段はオフで、ICMPブラックホールを検知したときにTCPの経路MTU探索をオンにする - [ping(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ping.8.html) · iputils · -M doはDFを付けて経路MTUより大きいパケットを拒否、-sでデータサイズを指定(デフォルトは56バイトで、これにICMPヘッダー8バイトが加わる) - [ping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) · Microsoft · /fはDFを付けて経路MTUの問題を探すのに使い、/lでデータサイズを指定 ### L6 サーバーのネットワークカード(原因9件) #### nic-irq · NIC割り込みの単一コア集中 · Single-queue NIC / no RSS NICがパケット到着の割り込みを1つのCPUコアにだけ送ると、そのコアがボトルネックになります。 - なぜ → すると → 画面では:受信キューが1つだけか、複数のコアに分散するRSSが無効 → 1つのコアが100%になり、パケットを時間内に取り出せない → 人が集中したときにサーバー全体でパケットロスと遅延(ワープ・入力遅延) - 症状:ワープ, 引き戻し, 入力遅延 / 要因:パケットロス, 遅延 - 誰に:サーバー全体 / いつ:人が集中したとき - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:RSS(NICが分散)・RPS(カーネルが分散)の設定、割り込みを複数のコアに分散、UDPはポートまで見てキューを分けるよう設定(ethtool -Nのrx-flow-hash udp4 sdfn)、割り込み処理のコアとゲームのティックスレッドのコアを分離、コアごとの%softを監視。 - 数値の目安:1つのコアがカーネル経由で処理できる量は、パケットサイズと設定によっておおよそ毎秒数十万パケットです。コアごとの使用率で、受信処理の割合(mpstatの%soft)が1つのコアにだけ偏っていればこのケースです。 - グラフでは:上限で頭打ち(コアごとの%soft、毎秒の受信パケット数) - 確認箇所:mpstat -P ALL 1でコアごとの%soft(ソフトウェア割り込み処理の割合)を確認し、/proc/interruptsでNICのキューごとの割り込みがどのコアに行っているか、ethtool -lでキュー数、ethtool -Sでキューごとのパケット数(名前はドライバーごとに異なる)を確認 - 該当する場合:1つのコアだけ%softが100%近くに張り付き、残りは空いていて、割り込みとパケットが1つのキューに集中。そこから毎秒の受信パケット数がそれ以上上がらない - 該当しない場合:%softが複数のコアに均等に分散していればこの原因ではない。CPUは空いているのにパケットロスがあれば「クラウドのPPS上限超過」か「リングバッファ不足」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:キューが複数あっても、ゲートウェイ・プロキシのように少数のアドレスからトラフィックの大半が来ると、1つのキューに集中します。UDPはNICのデフォルト設定がアドレスだけを見てキューを分けることがあり、ポートまで見るように変えないと均等に分散しません。 - 出典: - [Scaling in the Linux Networking Stack](https://docs.kernel.org/networking/scaling.html) · Linux kernel · RSS(NICが複数の受信キューに分散)・RPS(カーネルが分散)、キューごとに割り込みを分けて複数のコアに振り分ける設定、受信割り込みの処理がボトルネックならRSSを推奨 - [How to receive a million packets per second](https://blog.cloudflare.com/how-to-receive-a-million-packets/) · Cloudflare · 1つの受信キューが1つのコアにだけ行くと、そのコアが毎秒約35万〜43万パケットで頭打ちになった測定、NICがUDPをIPアドレスだけでハッシュして1つのキューに集中した事例 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · ethtool -N rx-flow-hash udp4でUDPのハッシュにポート(f・n)まで含めるオプション - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · %soft:CPUがソフトウェア割り込みの処理に使った時間の割合、-P ALLでコアごと #### nic-ring · リングバッファ不足 · RX ring buffer overflow NICがパケットを一時的に入れておくリングバッファが小さいと、瞬間的に集中したときにバッファがあふれて破棄されます。 - なぜ → すると → 画面では:リングバッファがデフォルト値(ドライバーごとにスロット256〜2,048個)のままで小さい → バースト時にCPUが取り出す前にバッファがあふれる → バーストの瞬間だけパケットロス(ワープ・スキル不発)。ゲームサーバーのログには痕跡がない - 症状:ワープ, 不発・ロールバック / 要因:パケットロス - 誰に:サーバー全体 / いつ:人が集中したとき - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:リングバッファを大きくする(ethtool -G)、破棄(drop)カウンターを監視(ethtool -Sのrx_missed_errorsなど、名前はドライバーごとに異なる)。 - 数値の目安:毎秒100万パケットが集中すると、スロット1,024個は約1msで埋まります。その間にCPUが一度でも遅れればあふれます。ほとんどのNICは数千個まで増やせます。 - グラフでは:不定期なスパイク(NICの受信破棄カウンター) - 確認箇所:ethtool -Sの受信破棄カウンター(rx_missed_errors、rx_fifo_errorsなど、名前はドライバーごとに異なる)とip -s -s linkのmissedを短い間隔で集め、ethtool -gで現在のリングサイズと最大値を確認 - 該当する場合:バーストの瞬間に破棄カウンターが増え、現在のリングサイズが最大値よりずっと小さい。リングを大きくすると破棄が減る - 該当しない場合:破棄カウンターが変わらないのにパケットロスがあれば、カーネルの次の段階(「カーネルのソケットバッファ不足」)かネットワーク区間。1つのコアの%softが100%なら「NIC割り込みの単一コア集中」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Linux Base Driver for Intel(R) Ethernet Network Connection (e1000)](https://docs.kernel.org/networking/device_drivers/ethernet/intel/e1000.html) · Linux kernel · e1000の受信ディスクリプタ(リングのスロット)はデフォルト256個、4,096個まで増やせる - [drivers/net/ethernet/intel/ice/ice.h (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/intel/ice/ice.h?h=v6.12) · Linux kernel · iceドライバーの受信ディスクリプタはデフォルト2,048個、最大8,160個 - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · バッファ不足でデバイスが破棄したパケットはrx_missed_errorsで集計、ドライバーごとの統計はethtool -Sで確認 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · -gでリングサイズ(現在値・最大値)を確認、-Gで変更、-Sでドライバーごとの統計 #### nic-coalesce · 過剰な割り込みコアレッシング · Interrupt coalescing CPUの負担を減らすために、パケットをまとめてから一度に知らせると、まとめる時間の分だけ遅れます。 - なぜ → すると → 画面では:NICが一定の時間・個数だけまとめてから通知 → まとめている間、パケットが待たされる → わずかな遅延の増加。通常は小さいが、過剰だとms単位 - 症状:入力遅延 / 要因:遅延 - 誰に:サーバー全体 / いつ:常に - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:適応型コアレッシング、ゲームサーバーに合った値に調整(ethtool -C)。 - 数値の目安:通常は数十〜数百µsです。ゲームではたいてい無視できる程度ですが、設定が過剰だとms単位まで大きくなります。 - グラフでは:最初から常に高い(同じデータセンター内の往復時間) - 確認箇所:ethtool -cで現在のコアレッシング設定(adaptive-rx、rx-usecs、rx-frames)を確認し、同じデータセンターのほかのサーバーとのpingの往復時間を、設定を変える前後で比較 - 該当する場合:rx-usecsが数百µs以上と大きく設定されていて、値を小さくすると同じデータセンター内の往復時間がその分だけ短くなる - 該当しない場合:小さくしても往復時間が変わらなければこの原因ではない - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Linux Driver for Intel(R) Ethernet Network Connection (e1000e)](https://docs.kernel.org/networking/device_drivers/ethernet/intel/e1000e.html) · Linux kernel · デフォルトは適応型の割り込み調整で毎秒4,000〜20,000回(間隔50〜250µs)、割り込みを減らすとCPUは節約できるが遅延が増える - [Linux Base Driver for Intel(R) Ethernet Network Connection (e1000)](https://docs.kernel.org/networking/device_drivers/ethernet/intel/e1000.html) · Linux kernel · RxIntDelayで受信割り込みを1.024µs単位で最大65,535まで(約67ms)遅らせることができ、値を大きくすると受信遅延が増える - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · ethtool -Cのadaptive-rx・rx-usecs・rx-frames設定 #### nic-cloud-pps · クラウドのPPS上限超過 · Cloud PPS / bandwidth allowance クラウドのサーバーには種類ごとに毎秒のパケット数・帯域幅の上限があり、超えると黙って破棄されます。 - なぜ → すると → 画面では:同時接続数が増えて、毎秒のパケット数がインスタンスの上限を超える → クラウドのネットワークが超過分を破棄 → 原因のわからないパケットロスでワープ・スキル不発。サーバーのCPUには余裕がある - 症状:ワープ, 不発・ロールバック / 要因:パケットロス - 誰に:サーバー全体 / いつ:人が集中したとき, 夜のピーク時間帯 - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発, 外部・外部 - ゲーム開発チームの対応:パケットをまとめる(1ティックのメッセージを1つのパケットに)、ごく小さなパケットを頻繁に送らない。 - インフラチームの対応:上限超過カウンターの確認(AWSはpps_allowance_exceeded、conntrack_allowance_exceededなど)・アラート、より大きなインスタンス、接続追跡の上限は追跡が発生しないセキュリティグループ構成で回避。 - 外部の対応:クラウド事業者に、インスタンスの種類ごとの毎秒パケット数・接続追跡の上限を問い合わせ。 - 数値の目安:上限はインスタンスのサイズごとに異なり、毎秒のパケット数の上限は公開されていないことが多いです。小さいインスタンスの「最大10Gbps」は、クレジットが残っている間(通常5〜60分)だけ使えるバースト速度で、普段のベースライン速度はずっと低くなります。 - グラフでは:上限で頭打ち(毎秒のパケット数、allowance超過カウンター) - 確認箇所:ethtool -SのENAカウンターpps_allowance_exceeded・bw_in_allowance_exceeded・bw_out_allowance_exceeded・conntrack_allowance_exceededを短い間隔で集め、毎秒のパケット数と一緒に確認。CloudWatchエージェントでこれらのカウンターを送信し、アラートを設定することもできる - 該当する場合:パケットロスが出た時刻にallowance超過カウンターが増え、毎秒のパケット数が一定の値から上がらない。サーバーのCPUには余裕がある - 該当しない場合:超過カウンターが変わらなければこの原因ではない。1つのコアの%softが100%なら「NIC割り込みの単一コア集中」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:conntrack_allowance_exceededは、接続追跡テーブルがいっぱいになって新しい接続を破棄したケースです。テーブルには余裕があるのに、アイドル接続だけが追跡の期限切れで切れる場合は「クラウドのセキュリティグループによる接続追跡の期限切れ」の項目を参照してください。 - 出典: - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · インスタンスごとに帯域幅・PPS・接続追跡の上限があり、超えるとキューにためてから破棄、pps_allowance_exceeded・conntrack_allowance_exceededカウンター - [Amazon EC2 instance network bandwidth](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-network-bandwidth.html) · AWS · 16 vCPU以下のインスタンスの「最大N Gbps」はネットワークI/Oクレジットで使うバースト(通常5〜60分)、クレジットが尽きるとベースラインの帯域幅に戻る #### nic-saturate · NIC帯域幅の飽和 · NIC bandwidth saturation 1Gbps・10Gbpsのカードを限界まで使うと、送信キューが長くなり、最終的に破棄されます。 - なぜ → すると → 画面では:ブロードキャストの増加で送信量がカードの限界に到達 → 送信キューが長くなり、あふれると破棄 → サーバー全体で遅延・パケットロス(入力遅延・ワープ) - 症状:入力遅延, ワープ / 要因:遅延, パケットロス - 誰に:サーバー全体 / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:送信量を減らす(関心領域、圧縮、差分のみ送信)。 - インフラチームの対応:カードの増強(より高速なNIC、クラウドならより大きなインスタンス)、NIC使用率のアラート。 - グラフでは:上限で頭打ち(NICの送信量、送信破棄) - 確認箇所:sar -n DEV 1のtxkB/sと%ifutil(インターフェース速度に対する使用率)をNICの速度・インスタンスの帯域幅と比較し、ip -s linkのTX droppedも一緒に確認 - 該当する場合:送信量がNIC・インスタンスの帯域幅付近で平らになり、そこから送信破棄とサーバー全体の遅延が増える - 該当しない場合:帯域幅に余裕があればこの原因ではない。小さなパケットが多くてパケットロスがあれば「クラウドのPPS上限超過」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · tx_dropped:リソース不足で送信中に破棄したパケット数 - [Amazon EC2 instance network bandwidth](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-network-bandwidth.html) · AWS · インスタンスが使える帯域幅はvCPU数(インスタンスサイズ)によって決まる - [sar(1) — Linux manual page](https://man7.org/linux/man-pages/man1/sar.1.html) · sysstat · -n DEVのrxkB/s・txkB/sと%ifutil(インターフェース速度に対する使用率) #### nic-noisy · 仮想化オーバーヘッド・ノイジーネイバー · Noisy neighbors in virtualization 同じ物理サーバー上のほかの仮想マシンがネットワーク・CPUを大量に使うと、自分のサーバーの処理が不規則に遅れます。 - なぜ → すると → 画面では:同じ物理サーバー上のほかの仮想マシンがリソースを大量に使用 → 自分の仮想マシンのパケット処理が不規則に遅れる → はっきりした原因がないのに、ときどきジッター(到着間隔のばらつき)が生じてカクつき - 症状:カクつき / 要因:ジッター - 誰に:サーバー全体 / いつ:ときどきランダムに - 主担当:インフラチーム・サーバーインフラ / 副担当:外部・外部 - インフラチームの対応:専有ホスト、性能が保証されたインスタンス、ジッターが続くインスタンスは停止してから再起動し、別のホストに移す。 - 外部の対応:クラウド事業者に問題のあるホストを報告。 - グラフでは:不定期なスパイク(同じデータセンター内の往復時間のジッター、%steal) - 確認箇所:同じデータセンターのほかのサーバーへpingを送り続けて往復時間のジッターを記録し、mpstatの%stealと合わせて、同じ構成のほかのインスタンスと比較 - 該当する場合:このインスタンスだけ往復時間のジッターや%stealが不規則に跳ね、同じ構成のほかのインスタンスは落ち着いている。停止してから再起動し、別のホストに移すと消える - 該当しない場合:同じ構成のインスタンスがすべて同じように跳ねるならホストの問題ではない。ゲームサーバー側の負荷やネットワーク区間を確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [How EC2 instance stop and start works](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-ec2-instance-stop-start-works.html) · AWS · インスタンスを停止してから起動すると、ほとんどの場合新しいホストに移る(専有ホストを除く) - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · %steal:ハイパーバイザーがほかの仮想CPUを実行している間、この仮想CPUがやむを得ず待たされた時間の割合 #### nic-host-maintenance · クラウドホストのメンテナンス・ライブマイグレーション · Cloud host maintenance / live migration クラウド事業者は物理サーバー(ホスト)をメンテナンスするとき、仮想マシンを別のホストに移したり(ライブマイグレーション)、一時的に止めたりします。その間はサーバー全体が止まり、止まる時間が長いと接続が切れます。 - なぜ → すると → 画面では:事業者がホストのメンテナンスや故障予測のために、仮想マシンを別のホストに移すか一時停止 → 移している間はCPU・メモリ・ネットワークが遅くなり、最後に仮想マシンが一瞬完全に止まる(事業者と方式によって1秒未満〜30秒前後) → サーバーの全員が同時にフリーズした後に早送り・ワープ、停止がタイムアウトより長ければ大量の切断 - 症状:フリーズ, 早送り, ワープ, 切断 / 要因:ストール, パケットロス - 誰に:サーバー全体 / いつ:ときどきランダムに - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発, 外部・外部 - ゲーム開発チームの対応:数秒の停止に耐えるタイムアウト、停止後に追いつくためのティック数に上限を設ける、経過時間はモノトニッククロック(monotonic clock)で計算、メンテナンス通知を受けたら進行状況を保存してユーザーを別のサーバーに移す手順。 - インフラチームの対応:メンテナンス通知の購読とアラート(Google Cloudのmaintenance-event、AWSのスケジュールされたイベント・AWS Health、Azure Scheduled Events)、通知を受けたらユーザーの少ない時間帯に前もってサーバーを入れ替える、事業者が許可していればメンテナンス時刻を調整(Azure Maintenance Configuration、種類によってはAWSのスケジュールされたイベント)、メンテナンス記録と障害記録を照合。 - 外部の対応:クラウド事業者にメンテナンスのスケジュールと影響範囲を確認、同じインスタンスで停止が繰り返されるなら報告。 - 数値の目安:Google Compute Engineは、ライブマイグレーションによる停止は通常1秒よりずっと短いとしており、停止中にシステム時刻が最大5秒先に飛ぶことがあります。メタデータのmaintenance-eventの値は、移行の60秒前に変わります(それ以前にこの値を一度以上取得している場合)。Azureは、再起動が不要なメンテナンスであればほぼ常に10秒未満、まれに(一般的なサイズでは18か月に1回以下)約30秒止まり、ライブマイグレーションは通常5秒を超えません。Azure Scheduled Eventsは、こうした停止(Freeze)を少なくとも15分前に通知します。ただし、ホストのハードウェアが突然故障した場合は、通知なしですぐに復旧を始めます。 - グラフでは:途切れた後にまとめて到着(サーバーの送受信パケット数、ティック間隔) - 確認箇所:停止した時刻を事業者の記録と照合。Google Cloudは監査ログのcompute.instances.migrateOnHostMaintenance、AWSはdescribe-instance-status・AWS Healthのスケジュールされたイベント、Azureはアクティビティログ(Activity Log)のMicrosoft.Compute/virtualMachines/liveMigration/actionと、VMの可用性メトリクス(VmAvailabilityMetric)が0に落ちた時刻。サーバー内では、停止中にメトリクス・ログが空白になっていないか、直後に時刻が飛んでいないか(時刻同期のログ)を確認 - 該当する場合:サーバー全体が止まった時刻が、事業者の記録したメンテナンス・マイグレーションの時刻と重なり、その数秒間はサーバー内のメトリクス・ログがすべて空白 - 該当しない場合:事業者の記録になく、短い停止が頻繁に繰り返されるなら「CPUスチール(仮想マシン)」。カーネルログにNICのリセットの記録があれば「NICドライバー・ファームウェアの問題」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:AWSはスケジュールされたイベントで通知します。system-rebootは再起動して新しいホストに移すことを、system-maintenanceはネットワーク・電源のメンテナンスで一時的に影響を受ける可能性があることを意味します。停止が数秒でも、その間にサーバーへ送ったパケットのACKを受け取れなかったクライアントは再送の待ち時間を2倍ずつ延ばしていくため、停止が解けた後もTCP接続はさらに長く止まったままになることがあります(「TCPのRTOと指数バックオフ」)。停止から復帰すると時刻が飛んで「システム時刻のジャンプ(NTPステップ)」につながることもあり、ロードバランサーのヘルスチェックが失敗してそのサーバーが一時的に外されることもあります。移行できないインスタンス(Google Cloudのベアメタルインスタンスなど)は、メンテナンス時に停止または再起動されます。 - 出典: - [Live migration process during maintenance events](https://cloud.google.com/compute/docs/instances/live-migration-process) · Google Cloud · ライブマイグレーションによる停止は通常1秒よりずっと短い、停止中にシステム時刻が最大5秒先に飛ぶ、移行中はディスク・CPU・メモリ・ネットワークの性能が一時的に下がる、ライブマイグレーションしないVMはメンテナンス時に終了(ベアメタルインスタンスは非対応) - [Query metadata server for maintenance event notices](https://cloud.google.com/compute/docs/metadata/getting-live-migration-notice) · Google Cloud · maintenance-eventのメタデータ値がライブマイグレーションの60秒前に変わる(ライブマイグレーションの設定で、前回のメンテナンス以降にこの値を一度以上取得している場合) - [Monitor and plan for a host maintenance event](https://cloud.google.com/compute/docs/instances/monitor-plan-host-maintenance-event) · Google Cloud · メンテナンス時に、監査ログにcompute.instances.migrateOnHostMaintenanceのシステムイベントが残る - [Scheduled events for Amazon EC2 instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-instances-status-check_sched.html) · AWS · スケジュールされたイベントの種類(system-rebootは再起動して新しいホストへ移動、system-maintenanceはネットワーク・電源のメンテナンスで一時的に影響)、メール・AWS Healthで通知、describe-instance-statusで確認、種類によっては時刻を調整可能 - [Maintenance and updates](https://learn.microsoft.com/en-us/azure/virtual-machines/maintenance-and-updates) · Microsoft Azure · 再起動なしのメンテナンスはほぼ常に10秒未満の停止、まれに(一般的なサイズでは18か月に1回以下)約30秒、ライブマイグレーションは通常5秒以下、停止後に時刻を自動同期、長いTCP接続が切れたり、相手が停止中のVMに送ったデータを指数バックオフで再送して復旧がさらに遅れたりすることがある、ロードバランサーのヘルスチェックが約10秒で異常と判定、アクティビティログのMicrosoft.Compute/virtualMachines/liveMigration/actionと停止中に0になるVmAvailabilityMetricで確認、Maintenance Configurationで適用時刻を選択 - [Scheduled Events for Linux VMs in Azure](https://learn.microsoft.com/en-us/azure/virtual-machines/linux/scheduled-events) · Microsoft Azure · Freeze(数秒の停止、CPU・ネットワークが止まることがある)は少なくとも15分前に通知、ホストのハードウェア故障時は通知期間なしですぐに復旧を開始 #### nic-reset · NICドライバー・ファームウェアの問題 · NIC hang / reset ドライバーのバグや機能の誤動作でカードが止まり、再起動している間はすべての送受信が途切れます。 - なぜ → すると → 画面では:ドライバーのバグ、オフロード機能の誤動作 → NICが止まって再起動(数秒) → そのサーバーの全員が一緒にフリーズした後、ワープするか切断 - 症状:フリーズ, 切断 / 要因:パケットロス - 誰に:サーバー全体 / いつ:ときどきランダムに, 長時間稼働するほど - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:カーネルログの「transmit queue … timed out」「Link is Down」の記録を確認・アラート、ドライバー・ファームウェアの更新、問題のある機能(オフロードなど)を無効化。 - グラフでは:途切れた後にまとめて到着(サーバーの送受信パケット数) - 確認箇所:dmesgでカーネルログから「NETDEV WATCHDOG … transmit queue N timed out」、ドライバーのリセット、「Link is Down」「Link is Up」の記録を探し、その時刻のサーバーの送受信パケット数を確認 - 該当する場合:止まった時刻のカーネルログに送信キューのタイムアウトやリンクのダウン・アップの記録があり、その数秒間は送受信パケット数が0 - 該当しない場合:カーネルログがきれいで、スイッチ側のポートも正常なら、ゲームサーバープロセスの停止(「サーバーGCの全停止」「デッドロック」)か「ネットワーク機器のフェイルオーバー」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [net/sched/sch_generic.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sched/sch_generic.c?h=v6.12) · Linux kernel · 送信キューが止まると、カーネルのウォッチドッグが「NETDEV WATCHDOG … transmit queue N timed out」を記録し、ドライバーのリセット関数を呼ぶ - [drivers/net/ethernet/intel/ixgbe/ixgbe_main.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c?h=v6.12) · Linux kernel · ixgbeドライバーが送信停止時にアダプターをリセットし、リンクが切れると「NIC Link is Down」を記録 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · ethtool -Kでオフロード機能を1つずつ無効にするオプション #### nic-offload · GRO/LROの結合待ちによる遅延 · GRO/LRO batching 複数のパケットを1つにまとめてCPUの負担を減らす機能です。設定によっては、小さなゲームのパケットが、一緒にまとめる次のパケットを少し待つことがあります。 - なぜ → すると → 画面では:NIC・カーネルが到着したパケットをまとめて処理 → ハードウェアによる結合(LRO)や結合の待ち時間の設定が有効だと、次のパケットを待って少し待機 → わずかな遅延の増加(たいてい数十µs以下) - 症状:入力遅延 / 要因:遅延 - 誰に:サーバー全体 / いつ:常に - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:ゲームのトラフィックに合わせて調整(LROを無効化、結合の待ち時間の設定を確認)、効果はたいてい小さいのでほかの原因より後に確認。 - グラフでは:最初から常に高い(同じデータセンター内の往復時間) - 確認箇所:ethtool -kでlro・groの状態を、デバイスのsysfs設定gro_flush_timeoutの値を確認し、変更の前後で同じデータセンター内の小さなパケットの往復時間を比較 - 該当する場合:LROが有効か、gro_flush_timeoutが0より大きく、無効にするか0にすると小さなパケットの往復時間が短くなる - 該当しない場合:変えても差が数µs以内ならこの原因ではない - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [NAPI](https://docs.kernel.org/networking/napi.html) · Linux kernel · gro_flush_timeoutを大きくすると、まとめて処理する代わりに負荷が低いときに遅延が生じる - [Linux Base Driver for the Intel(R) Ethernet 10 Gigabit PCI Express Adapters (ixgbe)](https://docs.kernel.org/networking/device_drivers/ethernet/intel/ixgbe.html) · Linux kernel · GROは受信トラフィックを大きな塊にまとめてCPUを節約する機能で、LROを発展させたもの - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · ethtool -Kのgro・lro on|off設定 ### L7 サーバーOS(カーネル)(原因14件) #### so-backlog · 接続待ちキュー(backlog)のあふれ · Listen backlog / SYN queue overflow メンテ明けに数万人が同時に接続すると、カーネルの接続待ちキュー(backlog)があふれ、接続要求が破棄されます。 - なぜ → すると → 画面では:メンテ終了と同時に、ゲームサーバーがacceptで接続を処理する速度を上回る勢いで接続が殺到 → カーネルの接続待ちキュー(backlog。サーバーのコードがlistenに渡した値とカーネルの上限のうち小さいほう)が満杯 → 接続要求が破棄されて再試行が繰り返され、接続不可・無限ロード - 症状:接続不可・無限ロード / 要因:パケットロス - 誰に:サーバー全体 / いつ:接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:コードでlistenに渡す値を増やす、接続を受け付けるスレッドがほかの処理で止まらないようにする、ログイン待機列の仕組み。クライアント:再試行の間隔を広げる(ランダムに分散)。 - インフラチームの対応:カーネルのsomaxconnを増やす(サーバーのコードのlistenの値と両方を上げないと効果がない)、SYN Cookieを有効にしておく、あふれた回数の監視(nstatのTcpExtListenOverflows)。 - 数値の目安:Linuxカーネルの上限(somaxconn)は5.4からデフォルト4,096(それ以前は128)ですが、サーバーのコードがlistenにそれより小さい値を渡すと、その値が上限になります。Linuxはキューが満杯になると、接続要求をエラーも返さずに黙って破棄します。クライアントのOSが1秒後から何度か再送するため、プレイヤーには「接続失敗」よりも長いロードに見えます。Windowsサーバーは拒否の応答を返すので、クライアントにはすぐに「接続失敗」と表示されます。 - グラフでは:接続直後・メンテ明けに急増(接続待ちキューのあふれ数(ListenOverflows)、接続試行数) - 確認箇所:nstat -azでTcpExtListenOverflows・TcpExtListenDropsの増加量を確認し、ss -ltnでリッスンソケットのRecv-Q(acceptを待っている接続数)とSend-Q(backlogの上限)を比較 - 該当する場合:接続が殺到した時刻にListenOverflowsが増え、リッスンソケットのRecv-QがSend-Qの値に張り付いている - 該当しない場合:ListenOverflowsが増えていなければこの原因ではない。接続は確立したのにロードが終わらなければ「ログイン殺到とN+1クエリ」、ちょうど一定の人数で入れなくなるなら「ファイルディスクリプタの上限」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · listenのbacklogはsomaxconnを超えると黙って切り詰められる、somaxconnのデフォルトは4,096(5.4から。以前は128)、キューが満杯のときは要求を無視してクライアントの再試行に任せることがある - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_syn_retries:接続要求(SYN)を、最初の再送待ちを1秒として何度か再送、tcp_abort_on_overflowはデフォルトで無効(あふれても拒否の応答を返さない)、tcp_syncookiesはデフォルトで有効 - [listen function (winsock2.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen) · Microsoft · Windowsではキューが満杯になると、クライアントがWSAECONNREFUSEDエラーを受け取る - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtListenOverflows:acceptキューが満杯で接続要求(SYN)を破棄した回数、このときTcpExtListenDropsも一緒に増える - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · ssのRecv-Q・Send-Qの値:リッスンソケットではacceptを待っている接続数とbacklogの上限、接続済みソケットではアプリがまだ読んでいないバイト数とACKを受け取っていない送信バイト数 #### so-fd · ファイルディスクリプタの上限 · File descriptor limit (ulimit) 接続ごとにファイルディスクリプタ(fd。OSが開いているファイルやソケットに付ける番号)が必要ですが、1つのプロセスが開けるfdの数には上限があります。 - なぜ → すると → 画面では:同時接続数がプロセスのファイルディスクリプタ上限に到達 → サーバーが新しい接続を受け付けられない(Too many open files)。ログファイルやDB接続を開く処理も同時に失敗 → ちょうど一定の人数から先は誰も入れない接続不可・無限ロード - 症状:接続不可・無限ロード / 要因:パケットロス - 誰に:サーバー全体 / いつ:接続直後・メンテ明け, 人が集中したとき - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:接続を終えるときにソケットを確実に閉じる(fdリークの防止)、acceptがEMFILE(fd不足)で失敗したら接続の受け付けをしばらく止めるか、あらかじめ確保しておいた予備のfdで受け付けてすぐ閉じる(同じ接続通知だけを繰り返し処理してCPUを浪費しないように)。 - インフラチームの対応:ulimit・サービス設定(systemdのLimitNOFILE)の確認、上限に近づいたときのアラート。 - 数値の目安:Linuxでは、サービスの設定を個別に変えない限り上限が1,024のままの場合がまだ多くあります。ゲームサーバーでは通常、数万〜数十万に引き上げます。Windowsにはこのような低いデフォルトの上限はありません。 - グラフでは:上限で頭打ち(プロセスが開いているfd数、同時接続数) - 確認箇所:pidstat -vでゲームサーバープロセスのfd-nr(開いているファイルディスクリプタ数)を、/proc/PID/limitsで開けるファイル数の上限を確認し、サーバーログからacceptの失敗(EMFILE、Too many open files)を探す - 該当する場合:fd数が上限値で頭打ちになり、その時刻からacceptがEMFILEで失敗している - 該当しない場合:fd数が上限に遠く及ばなければこの原因ではない。接続要求がカーネルで破棄されていれば「接続待ちキュー(backlog)のあふれ」、接続追跡の問題なら「サーバーのconntrackテーブル飽和」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:受け付けられなかった接続はカーネルの接続待ちキュー(backlog)にそのまま残るため、サーバーのコードによっては「新しい接続あり」の通知を受け続けてCPUを浪費することもあります。 - 出典: - [systemd-system.conf(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd-system.conf.5.html) · systemd · サービスのDefaultLimitNOFILEのデフォルト値は1024:524288(ソフトリミット1,024) - [accept(2) — Linux manual page](https://man7.org/linux/man-pages/man2/accept.2.html) · Linux man-pages · プロセスのfd上限に達するとacceptがEMFILEで失敗 - [Maximum Number of Sockets Supported](https://learn.microsoft.com/en-us/windows/win32/winsock/maximum-number-of-sockets-supported-2) · Microsoft · WindowsのWinsockはソケット数を利用可能なメモリ量でのみ制限 - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -vのfd-nr:プロセスが開いているファイルディスクリプタ数 - [proc_pid_limits(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_pid_limits.5.html) · Linux man-pages · /proc/PID/limitsに、プロセスごとのリソース上限のソフト・ハードの値 #### so-sockbuf · カーネルのソケットバッファ不足 · Small socket buffers 送受信バッファが小さいと、バーストトラフィックが集中したときに、UDPで受信したパケットは破棄され、TCPの送信はバッファに空きがなくブロックされます。 - なぜ → すると → 画面では:SO_SNDBUF・SO_RCVBUFがデフォルト値のまま、または小さすぎる → バーストや、受信スレッドが一瞬止まった間にUDPの受信バッファがあふれて破棄、TCPは送信バッファに空きがなく待機 → ワープ(UDPのパケットロス)または早送り(TCPの待機) - 症状:ワープ, 早送り / 要因:パケットロス, ストール - 誰に:サーバー全体 / いつ:人が集中したとき - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:トラフィックに見合ったバッファサイズ(SO_SNDBUF・SO_RCVBUF)をコードで設定、TCPはサイズを明示するとLinuxの自動サイズ調整が無効になるので注意、大きくしすぎると古いデータがバッファにたまって遅延が増えるので適度に、受信スレッドが止まらないようにする。 - インフラチームの対応:カーネルの上限(rmem_max・wmem_max。コードで設定したバッファサイズもこの値を超えられない)とデフォルト値(rmem_default)の調整、バッファあふれのカウンター(RcvbufErrors)の監視。 - 数値の目安:LinuxのUDP受信バッファのデフォルト値は約208KBです。小さなパケットでも1個でカーネルメモリを実際のサイズよりはるかに多く消費するため、数十〜数百個で満杯になります。毎秒10万個を受信するサーバーなら、受信スレッドが数ms止まるだけであふれます。 - グラフでは:不定期なスパイク(UDP受信バッファのあふれ(UdpRcvbufErrors)) - 確認箇所:nstat -azでUdpRcvbufErrorsの増加量を、ss -uamnでskmem(rbは受信バッファサイズ、dはソケットに入れられず破棄したパケット数)を確認し、TCPはss -tmのskmemで送信待ちメモリ(w)が送信バッファサイズ(tb)に達しているかを確認 - 該当する場合:バーストや受信スレッドが止まった時刻にUdpRcvbufErrors(またはソケットのd)が増え、rbがデフォルト値(約208KB)付近。TCPはwがtbに張り付いたままsendがブロック - 該当しない場合:カウンターが増えていないのにパケットロスがあれば、NICの段階(「リングバッファ不足」)かネットワーク区間 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · SO_RCVBUF・SO_SNDBUFのデフォルト値はrmem_default・wmem_default、上限はrmem_max・wmem_max、設定値はカーネルが2倍にして確保 - [include/net/sock.h (Linux v6.18)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/sock.h?h=v6.18) · Linux kernel · デフォルトのソケットバッファを、sk_buffのオーバーヘッドを含む256バイトのパケット256個分(SKB_TRUESIZE(256)×256)と定義、小さなフレームもsk_buff+MTU分として計算される(約208KBはx86-64での計算値) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rmem・tcp_wmem:SO_RCVBUF・SO_SNDBUFを明示すると、そのソケットの自動サイズ調整が無効になる - [net/ipv4/udp.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/udp.c?h=v6.12) · Linux kernel · UDPの受信キューがソケットバッファサイズを超えると即座に破棄し、RcvbufErrorsを加算 - [net/ipv4/proc.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatが表示するカウンター名:UdpグループのRcvbufErrors・SndbufErrors - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · -mのskmem:rbは受信バッファサイズ、tbは送信バッファサイズ、wは送信待ちメモリ、dはソケットに入れる前に破棄したパケット数 #### so-context · スレッド過多とコンテキストスイッチ · Thread oversubscription, context switching コア数よりはるかに多いスレッドを動かすと、OSがそれらを切り替えて実行するだけでCPUを消費します。 - なぜ → すると → 画面では:接続ごとにスレッドを作るなどして、スレッドが数百〜数千個 → コンテキストスイッチ(実行するスレッドの切り替え)のコストとキャッシュミスが増加 → CPUはビジーなのにスループットは低く、ティックがばらついてカクつき・スローモーション - 症状:カクつき, スローモーション / 要因:ストール, ジッター - 誰に:サーバー全体 / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:コア数に合わせたスレッド数、非同期I/O(epoll・IOCP)。 - インフラチームの対応:コンテキストスイッチ回数と実行待ちスレッド数(vmstatのcs・r)の監視。 - 数値の目安:コンテキストスイッチは1回あたり数µsで、その後のキャッシュミスのコストまで含めるとさらに大きくなります。 - グラフでは:人数・負荷に連動して上昇(毎秒のコンテキストスイッチ数、実行待ちスレッド数) - 確認箇所:vmstat 1のcs(毎秒のコンテキストスイッチ数)・r(実行中またはCPU待ちの数)をコア数と比較し、pidstat -w -tでゲームサーバーのスレッドごとの自発的(cswch/s)・非自発的(nvcswch/s)コンテキストスイッチを確認 - 該当する場合:同時接続数が増えるとrがコア数を大きく上回り、csも急上昇し、非自発的コンテキストスイッチの多いスレッドが数百個ある - 該当しない場合:rがコア数以下にとどまればこの原因ではない。自発的なスイッチだけが多ければ、スレッドがロックやI/Oを待っている(「ロック競合」「ブロッキングI/O構造」) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Quantifying The Cost of Context Switch (ExpCS 2007)](https://www.usenix.org/legacy/events/expcs07/papers/2-li.pdf) · ACM · コンテキストスイッチの直接コストは約3.8µs、キャッシュへの影響まで含めた間接コストは数µs〜1,000µs以上(測定環境での値) - [vmstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/vmstat.8.html) · procps-ng · cs(毎秒のコンテキストスイッチ数)とr(実行中または実行待ちのプロセス数)の項目 - [I/O Completion Ports](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports) · Microsoft · あらかじめ作ったスレッドプールとIOCPで多数の非同期I/Oを処理し、同時に動くスレッド数をCPUの並行度に合わせる - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -wのcswch/sは資源を待って自ら停止した自発的コンテキストスイッチ、nvcswch/sはタイムスライスを使い切って強制的に切り替えられた非自発的コンテキストスイッチ、-tでスレッドごと #### so-steal · CPUスチール(仮想マシン) · CPU steal time 物理サーバー(ハイパーバイザー)が仮想マシンのCPU時間を一時的にほかの仮想マシンに回している間(CPUスチール)、ゲームサーバーが止まります。 - なぜ → すると → 画面では:同じホスト上のほかの仮想マシンがCPUを大量に使用 → 自分の仮想マシンが数ms〜数十msずつ実行機会を失う → 原因不明のティック時間の急増で、カクつき・フリーズ - 症状:カクつき, フリーズ / 要因:ストール - 誰に:サーバー全体 / いつ:ときどきランダムに - 主担当:インフラチーム・サーバーインフラ / 副担当:外部・外部 - インフラチームの対応:steal指標(top・vmstatのst)の監視、専用コア・専有ホスト、CPUクレジットが尽きると遅くなるバースト可能インスタンスを避ける、stealが高止まりするインスタンスは停止してから起動し直し、別のホストへ移す。 - 外部の対応:stealが高止まりするホストをクラウド事業者に報告。 - グラフでは:不定期なスパイク(%steal、サーバーのティック時間) - 確認箇所:mpstat -P ALL 1の%stealを、サーバーのティック時間と同じ時間軸に並べて確認 - 該当する場合:ティックが跳ねた時刻に%stealも跳ね、停止してから起動し直して別のホストに移すと減る - 該当しない場合:%stealが0に近いのにティックが跳ねるなら、ゲームサーバー内部の原因(「サーバーGCの全停止」「ロック競合」)。コンテナなら「コンテナのCPUスロットリング(CFSクォータ)」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · steal:仮想化環境で、ほかのOSが実行されていたために奪われた時間 - [Standard mode for burstable performance instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-standard-mode.html) · AWS · バースト可能インスタンスはクレジットを使ってベースライン性能を超え、クレジットが尽きるとCPU使用率がベースラインの水準まで下がる - [How EC2 instance stop and start works](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-ec2-instance-stop-start-works.html) · AWS · インスタンスを停止してから起動すると、ほとんどの場合は新しいホストに移される - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · %steal:ハイパーバイザーがほかの仮想CPUを実行している間、この仮想CPUがやむを得ず待たされた時間の割合 #### so-cpu-quota · コンテナのCPUスロットリング(CFSクォータ) · Container CPU throttling (CFS quota) コンテナにCPU上限をかけると、決まった周期(通常100ms)の中でクォータを使い切った時点から、残りの時間は強制的に止められます(スロットリング)。 - なぜ → すると → 画面では:Kubernetesなどで、ゲームサーバーのコンテナにCPU上限(limit)を設定している → ティックの計算が集中した瞬間にクォータを使い切り、次の周期まで数十ms停止 → 平均CPUは低いのにティックが周期的に跳ねて、カクつき・スローモーション - 症状:カクつき, スローモーション / 要因:ストール, ジッター - 誰に:サーバー全体 / いつ:人が集中したとき, ときどきランダムに - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ワーカースレッド数をCPU上限に合わせる(ランタイムがホスト全体のコア数分のスレッドを作らないように)。 - インフラチームの対応:CPU上限に余裕を持たせるか、上限を外して専用コアを割り当てる、スロットリング回数(nr_throttled)の監視。 - 数値の目安:上限2コアのサーバーでスレッド8個が同時に動くと、100ms周期のクォータを25msで使い切り、残りの75msは止まります。 - グラフでは:人数・負荷に連動して上昇(スロットリング回数(nr_throttled)、サーバーのティック時間) - 確認箇所:コンテナのcgroupのcpu.statで、nr_throttled・throttled_usec(cgroup v1ではnr_throttled・throttled_time)の増加量をサーバーのティック時間と並べて確認 - 該当する場合:平均CPU使用率は上限より低いのにnr_throttled・throttled_usecが増え続け、ティックが跳ねた時刻と重なる - 該当しない場合:nr_throttledが増えなければこの原因ではない。仮想マシン自体が待たされているなら「CPUスチール(仮想マシン)」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [CFS Bandwidth Control](https://docs.kernel.org/scheduler/sched-bwc.html) · Linux kernel · 周期ごとに割り当てられたクォータを使い切ると次の周期までスレッドが停止(スロットリング)、デフォルトの周期は100ms、nr_throttledの統計 - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · cpu.maxは「$MAX $PERIOD」(クォータ、周期)の形式で、デフォルト値は「max 100000」(100ms周期) - [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) · Kubernetes · コンテナのCPU limitは、カーネルがCPUスロットリングで強制するハードリミット #### so-cstate · サーバーの電源管理(C-state・周波数制御)による遅延スパイク · CPU power management latency (C-states, frequency scaling) 使われていないCPUコアは、電力を節約するために深い省電力状態(C-state)に入り、周波数も下げます。パケットやタイマーが来ると、復帰して周波数を上げるまでに時間がかかるため、小さなパケットの処理に遅延が加わります。 - なぜ → すると → 画面では:OSの周波数制御ポリシー(governor)やBIOSの電源設定が、深いC-stateと低い周波数を許可している → 休んでいたコアが深い省電力状態から復帰するたびに最大数百µs遅れ、周波数が低く固定されているとティックの計算自体が遅くなる → 普段は体感しにくいが、サーバー間の呼び出しが多いと積み重なり、空いているときにかえって応答が遅くなる入力遅延。周波数が低く固定されていると、人が集中したときにティックが遅れてスローモーション - 症状:入力遅延, スローモーション / 要因:遅延, ジッター, ストール - 誰に:サーバー全体 / いつ:常に, ときどきランダムに - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:BIOSの電源設定を性能重視に、OSのgovernorをperformanceに(cpufreqのscaling_governor)、遅延に敏感なサーバーでは深いC-stateを制限(tunedのlatency-performanceプロファイル、PM QoSの/dev/cpu_dma_latency、カーネルパラメータintel_idle.max_cstate)、変更後は同じデータセンター内の往復時間・ティック時間のジッターと消費電力を合わせて比較。 - 数値の目安:Linux 6.12のintel_idleドライバーにある表によると、IntelのサーバーCPUの浅いC1は復帰に1〜2µs、深いC6は133µs(Skylake-SP)〜290µs(Sapphire Rapids)かかります。1回あたりはわずかですが、1つのリクエストが複数のサーバーを経由すると、その分だけ積み重なります。カーネルは予想されるアイドル時間が長いほど深い状態を選ぶため、パケットがまばらにしか来ない空いたサーバーほど頻繁に起きます。汎用cpufreqのpowersave governorは、許可された範囲で最も低い周波数に固定します(intel_pstateにある同じ名前のアルゴリズムは、負荷に応じて調整します)。 - グラフでは:最初から常に高い(同じデータセンター内の往復時間、コアの周波数) - 確認箇所:cpupower monitorでコアごとのC-state滞在率と実際の周波数を確認し、/sys/devices/system/cpu/cpu0/cpuidle/以下の各stateのname・latency(復帰にかかるµs)・usage、cpufreqのscaling_governor、tuned-adm activeで現在のプロファイルを確認 - 該当する場合:空いているときにコアが最も深いC-stateに長くとどまるか、周波数が最低付近に固定されており、performance governor・浅いC-stateに変えると小さなリクエストの往復時間とジッターが減る - 該当しない場合:変えても差が数十µs以内なら、この原因は無視してよい。ms単位で跳ねるなら「CPUスチール(仮想マシン)」かほかの層 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:オンプレミスのベアメタルサーバーでは、BIOS(ファームウェア)の電源設定とOSの設定を両方確認します。クラウドでは一部のインスタンスタイプだけがOSからC-state・周波数を変更でき、AWSはデフォルト設定が最大性能寄りなので、ほとんどの場合はそのままで構いません。Red Hat系のtunedにあるlatency-performanceプロファイルは、governorをperformanceにし、PM QoSで浅いC-stateだけを使うようにします。省電力を無効にすると消費電力が増えるため、遅延に敏感なサーバーだけに適用します。 - 出典: - [CPU Idle Time Management](https://docs.kernel.org/admin-guide/pm/cpuidle.html) · Linux kernel · 省電力状態ごとに復帰時間(exit latency)と最小滞在時間(target residency)があり、予想アイドル時間に合わせて深い状態を選ぶ、sysfsのstateごとのlatency・usage・time、PM QoS(/dev/cpu_dma_latency)とintel_idle.max_cstateで深い状態を制限 - [drivers/idle/intel_idle.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/idle/intel_idle.c?h=v6.12) · Linux kernel · IntelのサーバーCPUのC-stateごとの復帰時間:Skylake-SP C1 2µs・C1E 10µs・C6 133µs、Ice Lake C6 170µs、Sapphire Rapids C1 1µs・C6 290µs - [CPU Performance Scaling](https://docs.kernel.org/admin-guide/pm/cpufreq.html) · Linux kernel · scaling_governorでgovernorを確認・変更、performanceは許可範囲で最も高い周波数、powersaveは最も低い周波数を要求 - [intel_pstate CPU Performance Scaling Driver](https://docs.kernel.org/admin-guide/pm/intel_pstate.html) · Linux kernel · intel_pstateのpowersaveアルゴリズムは、汎用のpowersave governorと違って負荷に応じて調整(schedutil・ondemandに近い) - [Chapter 2. Getting started with TuneD](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/monitoring_and_managing_system_status_and_performance/getting-started-with-tuned_monitoring-and-managing-system-status-and-performance) · Red Hat · latency-performanceプロファイルは省電力機能を無効にしてgovernorをperformanceにし、PM QoSで浅いC-stateだけを使わせる、tuned-adm activeで現在のプロファイルを確認 - [tools/power/cpupower/man/cpupower-monitor.1 (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/power/cpupower/man/cpupower-monitor.1?h=v6.12) · Linux kernel · cpupower monitor:コアごとの周波数と省電力状態の統計 - [Processor state control for Amazon EC2 Linux instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/processor_state_control.html) · AWS · 一部のインスタンスタイプだけがOSからC-state・P-stateを制御でき、遅延を減らすために変更できる、デフォルト設定は最大性能でほとんどのワークロードに適している、Gravitonは固定周波数のためOSからは制御しない #### so-oom · OOM Killer · Out-of-memory killer Linuxはメモリが尽きると、メモリを最も多く使っているプロセスを選んで強制終了します。たいていはゲームサーバーです。 - なぜ → すると → 画面では:リークや急増でメモリが枯渇、またはコンテナのメモリ上限に到達 → カーネルがゲームサーバーのプロセスを強制終了 → そのサーバーの全員が同時に切断、直近の進行がロールバックされることもある - 症状:切断, 不発・ロールバック / 要因:ストール - 誰に:サーバー全体 / いつ:長時間稼働するほど, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:リークの修正、メモリ使用量に上限を設け、近づいたらデータを保存して正常終了する手順。 - インフラチームの対応:メモリのアラート、コンテナのメモリ上限を実際の使用量に合わせて設定、先に終了させる順序の調整(oom_score_adj)。 - 数値の目安:カーネルログ(dmesg)に「Out of memory: Killed process」が残り、KubernetesではOOMKilledと表示されます。WindowsにはOOM Killerがなく、メモリ確保に失敗してサーバーがエラーで落ちることが多いです。 - グラフでは:接続が一斉に切れる(接続数、メモリ使用量) - 確認箇所:dmesgの「Out of memory: Killed process」の記録、KubernetesならPodステータスのOOMKilled、cgroup v2ならmemory.eventsのoom_killの増加を、接続が切れた時刻と突き合わせて確認 - 該当する場合:接続が一斉に切れた時刻にゲームサーバーのプロセスをkillした記録があり、その直前までメモリ使用量が上限に向かって上がっている - 該当しない場合:OOMの記録がないのにプロセスが落ちていれば、「サーバークラッシュ」の手順でクラッシュログ・コアダンプを確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [mm/oom_kill.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/oom_kill.c?h=v6.12) · Linux kernel · メモリを最も多く使っているプロセスが最も高いスコアになるよう計算(oom_score_adjを反映)、終了させるときに「Out of memory: Killed process …」を記録 - [Assign Memory Resources to Containers and Pods](https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/) · Kubernetes · コンテナがメモリのlimitを超えて使い続けると終了させられ、ステータスがOOMKilledと表示される - [Pushing the Limits of Windows: Virtual Memory](https://learn.microsoft.com/en-us/archive/blogs/markrussinovich/pushing-the-limits-of-windows-virtual-memory) · Microsoft · Windowsではコミット上限に達するとメモリをコミットする確保が失敗し、アプリのエラーやシステム障害につながることがある - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · memory.eventsのoom_kill:このcgroupでOOM Killerがkillしたプロセス数 #### so-reclaim · メモリの回収・コンパクションによる停止 · Memory compaction / reclaim stalls (THP) OSがヒュージページ(huge page)を作るためにメモリをコンパクションしたり、空きメモリを回収したりしている間、プロセスが止まります。 - なぜ → すると → 画面では:空きメモリが減る、またはヒュージページ機能(THP)がメモリのコンパクションを実行 → メモリを要求したスレッドが、回収・コンパクションが終わるまで待機 → 不規則なサーバーの停止(数ms〜数百ms) - 症状:フリーズ, カクつき / 要因:ストール - 誰に:サーバー全体 / いつ:長時間稼働するほど, ときどきランダムに - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:実行中の大きなメモリ確保を減らす(起動時にまとめて確保して再利用)。 - インフラチームの対応:ヒュージページ(THP)は必要な箇所だけで使うように設定(madvise)、空きメモリの基準を引き上げる(vm.min_free_kbytesなど)。 - グラフでは:不定期なスパイク(サーバーのティック時間、メモリPSI) - 確認箇所:/proc/pressure/memoryのsome・full(メモリ待ちで停止した時間の割合)と/proc/vmstatのcompact_stallの増加量をサーバーのティック時間と並べて確認し、/sys/kernel/mm/transparent_hugepage/defragの設定を確認 - 該当する場合:ティックが跳ねた時刻にメモリPSIが上がり、compact_stallが増える。defragがalways - 該当しない場合:PSIとcompact_stallが変わらなければこの原因ではない。スワップの使用量が増えていれば「スワップ」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Transparent Hugepage Support](https://docs.kernel.org/admin-guide/mm/transhuge.html) · Linux kernel · defrag=alwaysだと、THPの確保に失敗したときにその場でメモリの回収・コンパクションを行って停止、madviseでは要求した領域だけがそうなる - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · min_free_kbytes:カーネルが確保しておく最小の空きメモリ(ウォーターマーク)の基準 - [PSI - Pressure Stall Information](https://docs.kernel.org/accounting/psi.html) · Linux kernel · /proc/pressure/memoryのsome(一部のタスクがメモリ待ちで停止した時間の割合)・full(すべてのタスクが停止した時間の割合) #### so-timejump · システム時刻のジャンプ(NTPステップ) · Wall-clock jump (NTP step) サーバーの時刻が一度に数秒前後に調整されると、システム時刻に依存するタイマーが一斉に発火したり止まったりします。 - なぜ → すると → 画面では:時刻同期が時刻を一度に大きく調整 → タイマーがまとめて発火したり止まったりし、タイムアウトが誤って判定される → バフ・クールタイムの異常、一斉に切断、早送り - 症状:早送り, 切断, 不発・ロールバック / 要因:ストール - 誰に:サーバー全体 / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:経過時間・タイムアウト・クールタイムは、飛んだり戻ったりしないモノトニッククロック(monotonic clock)で計算、ウォールクロック(wall clock)は表示・記録用に限る。 - インフラチームの対応:時刻は徐々に調整(chronyのmakestepは起動直後だけ)、時刻同期の状態(時刻のずれ)の監視。 - 数値の目安:ntpdは差が0.128秒を超えると一度に合わせ、それより小さければ、1秒の差を解消するのに30分余りかかる速度で徐々に合わせます。最近よく使われるchronyは、推奨設定(makestep)では起動直後の数回だけ一度に合わせ、それ以降は徐々に合わせます。仮想マシンが一時停止から復帰したときにも時刻が飛びます。 - グラフでは:不定期なスパイク(タイマーの発火数・切断数、時刻調整の記録) - 確認箇所:時刻同期サービスのログから時刻を一度に大きく調整した記録を探し、異常が起きた時刻と突き合わせる。chronyはlogchangeで指定した値(デフォルト1秒)より大きく調整するとsyslogに記録 - 該当する場合:バフ・クールタイムの異常、一斉の切断、早送りが起きた時刻に時刻調整の記録があり、調整幅が異常の大きさと近い - 該当しない場合:時刻調整の記録がなければこの原因ではない。仮想マシンなら一時停止から復帰した場合(「クラウドホストのメンテナンス・ライブマイグレーション」)も確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [ntpd - Network Time Protocol (NTP) daemon](https://www.ntp.org/documentation/4.2.8-series/ntpd/) · Network Time Foundation · ステップのしきい値128msを超える差は一度に合わせ、それより小さければ徐々に合わせる、毎秒0.5msずつなので1秒合わせるのに2,000秒(約33分)かかる - [chrony – Frequently Asked Questions](https://chrony-project.org/faq.html) · chrony · makestep 1 3のように起動直後の数回だけステップを許可するのが推奨、一時停止から再開した仮想マシンは時刻がずれたまま復帰することがある - [clock_gettime(2) — Linux manual page](https://man7.org/linux/man-pages/man2/clock_gettime.2.html) · Linux man-pages · CLOCK_MONOTONICはシステム時刻の不連続なジャンプの影響を受けず、逆戻りしない - [chrony.conf(5)](https://chrony-project.org/doc/4.6/chrony.conf.html) · chrony · logchange:この値(デフォルト1秒)より大きく時刻を調整するとsyslogに記録 #### so-cron · 定期実行ジョブ · Cron jobs (log rotation, backup, scans) 毎日同じ時刻に動くログ圧縮・バックアップ・セキュリティスキャンが、CPUとディスクを占有します。 - なぜ → すると → 画面では:決まった時刻にOSのジョブが実行される → CPU・ディスクをゲームサーバーと取り合う → 毎日早朝4時のように、決まった時刻にカクつき・スローモーション - 症状:カクつき, スローモーション / 要因:ストール - 誰に:サーバー全体 / いつ:一定の周期で - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:ジョブの実行時刻を分散、優先度を下げる(nice、ionice)、ゲームサーバーから切り離す(別のサーバーで実行)。 - グラフでは:周期的なスパイク(CPU使用率、ディスクI/Oキュー、サーバーのティック時間) - 確認箇所:crontabとsystemctl list-timersで定期実行ジョブの実行時刻を集め、ティックが跳ねた時刻にpidstat -u -dでどのプロセスがCPU・ディスクを使っているかを確認 - 該当する場合:ティックが毎日(または毎時)同じ時刻に跳ね、その時刻に定期実行ジョブのプロセスがCPU・ディスクを占有している - 該当しない場合:跳ねる時刻が毎日の決まった時刻と一致しなければこの原因ではない。数秒・数分の間隔で跳ねるなら「サーバーGCの全停止」「タイマーの一斉発火」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [ionice(1) — Linux manual page](https://man7.org/linux/man-pages/man1/ionice.1.html) · util-linux · idleクラスのジョブは、ほかのプログラムがディスクを使っていないときだけI/Oを割り当てられる - [systemd.timer(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd.timer.5.html) · systemd · RandomizedDelaySecで定期実行ジョブの時刻をランダムに遅らせ、負荷の集中を減らす - [systemctl(1) — Linux manual page](https://man7.org/linux/man-pages/man1/systemctl.1.html) · systemd · list-timers:タイマーユニットを次回の実行時刻順に表示 - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -uはCPU、-dはディスクI/Oをプロセスごとに表示 #### so-os-update · OS・カーネル・ドライバー・ファームウェアのアップデート後の性能変化 · Performance regression after OS / kernel / driver / firmware update ゲームのコードは変わっていないのに、サーバーのOS・カーネル・ドライバー・ファームウェアをアップデートしてから遅くなるケースです。アップデートによって、デフォルト値、スケジューラー、CPU脆弱性の緩和策(mitigations)、ドライバーの動作が変わることがあります。 - なぜ → すると → 画面では:定期的なセキュリティパッチや新しいサーバーイメージで、カーネル・ドライバー・ファームウェアが変わる → デフォルト値やスケジューラーが変わったり新しい脆弱性の緩和策が有効になったりして、同じ処理により多くのCPU時間がかかり、スレッドにCPUが割り当てられる順序も変わる → 問題なかったサーバーがアップデートした日から常に少し遅くなって入力遅延、人が集中するとカクつき・スローモーション - 症状:入力遅延, カクつき, スローモーション / 要因:遅延, ストール, ジッター - 誰に:サーバー全体 / いつ:常に, 人が集中したとき - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:アップデートは一部のサーバーに先に適用し、ティック時間・遅延・CPU使用率を以前のバージョンと比べてから範囲を広げる、ゲームのアップデートとは別の日にデプロイ、アップデート前後のカーネル・ドライバー・ファームウェアのバージョンと主要なsysctlの値を記録、問題が出たら以前のカーネルで起動して確認、緩和策を無効にする設定(mitigations=off)はセキュリティリスクを検討して決める。 - 数値の目安:カーネルのバージョンが変わると、デフォルトの動作も変わります。たとえばLinuxは6.6以降、スケジューラーをCFSからEEVDFへ移行し始め、接続待ちキューの上限(somaxconn)のデフォルト値も5.4で128から4,096に変わりました。CPU脆弱性の緩和策は、カーネルからプログラムに戻るとき(システムコールを終えるたび)や、コンテキストスイッチ・仮想マシンの切り替えのときにCPU内部のバッファを消去するなどの処理を追加するため、パケットごとにシステムコールを呼ぶネットワークサーバーほど影響を受けます。一部の脆弱性は、完全に防ぐにはSMT(1つのコアを2つのスレッドのように使う機能)を無効にする必要があり、SMTを無効にすると処理内容によっては性能が大きく下がります。カーネルパラメータmitigations=offはこれらの緩和策をすべて無効にして性能を取り戻しますが、脆弱性にさらされます。 - グラフでは:ある時点から階段状に上昇(サーバーのティック時間、CPU使用率、同じ負荷での遅延) - 確認箇所:パッケージマネージャーのアップデート履歴と再起動時刻、uname -rで確認したカーネルバージョン、ethtool -iで確認したNICドライバーの情報を、遅延が上がった時刻と突き合わせる。アップデートしたサーバーとしていないサーバーを同じ負荷でmpstat・pidstatを使って比較し、/sys/devices/system/cpu/vulnerabilities/の緩和策の状態も比較 - 該当する場合:遅延・CPU使用率が、アップデート後に再起動した時刻から一段上がってそのまま続き、同じ負荷でアップデートしたサーバーだけが高い。以前のカーネル・ドライバーで起動すると元に戻る - 該当しない場合:アップデートしたサーバーとしていないサーバーが同じ負荷で同じように遅ければこの原因ではない。同じ日にゲームのアップデートもデプロイし、ユーザーあたりのパケット数・サイズが変わっていれば「アップデートによるトラフィックパターンの変化」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:緩和策の状態は/sys/devices/system/cpu/vulnerabilities/以下のファイルで確認します。デフォルト(mitigations=auto)はSMTを有効にしたまま緩和しますが、auto,nosmtにすると脆弱なCPUではSMTを無効にするため、カーネルを更新した後に論理コア数が半分に減ることがあります。ゲームのアップデートと同じ日にOSをアップデートすると原因の切り分けが難しくなるので、別々にデプロイします。 - 出典: - [The kernel’s command-line parameters](https://docs.kernel.org/admin-guide/kernel-parameters.html) · Linux kernel · mitigations=:offはCPU脆弱性の緩和策をすべて無効にして性能を上げるが脆弱性にさらされる、デフォルトのautoはSMTを有効にしたまま緩和、auto,nosmtは必要ならSMTを無効にする - [MDS - Microarchitectural Data Sampling](https://docs.kernel.org/admin-guide/hw-vuln/mds.html) · Linux kernel · 緩和策は、カーネルからユーザー空間に戻るときと仮想マシンに入るときにCPUのバッファを消去、/sys/devices/system/cpu/vulnerabilities/以下のファイルで脆弱性・緩和策の状態を確認、完全に防ぐにはSMTを無効にする必要があるCPUが多く、SMTを無効にすると処理内容によっては性能への影響が大きい - [Spectre Side Channels](https://docs.kernel.org/admin-guide/hw-vuln/spectre.html) · Linux kernel · 緩和のために、コンテキストスイッチ・仮想マシンの切り替え時に分岐予測バッファを消去し、強力な緩和策はすべてのプログラムにオーバーヘッドを加える - [EEVDF Scheduler](https://docs.kernel.org/scheduler/sched-eevdf.html) · Linux kernel · Linuxは6.6以降、スケジューラーをCFSからEEVDFへ移行し始めた - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · somaxconnのデフォルト値がLinux 5.4で128から4,096に変更 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · ethtool -iでネットワークデバイスのドライバー情報を取得 #### so-conntrack · サーバーのconntrackテーブル飽和 · conntrack table full Linuxのファイアウォールがすべての接続を記録する接続追跡(conntrack)テーブルが上限に達すると、新しいパケットを破棄します。 - なぜ → すると → 画面では:接続の殺到や短い接続の繰り返しで、接続の記録が増加 → テーブルが満杯になり、新しい接続と一部のパケットを破棄 → 接続不可、原因不明のパケットロスでワープ - 症状:接続不可・無限ロード, ワープ / 要因:パケットロス - 誰に:サーバー全体 / いつ:接続直後・メンテ明け, 人が集中したとき - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:短い接続を減らす(サーバー間の呼び出しは接続を再利用)。クライアント:接続に失敗したり切れたりしたら、再試行の間隔を広げながらランダムに分散。 - インフラチームの対応:テーブルサイズ(nf_conntrack_max)を増やす、ゲームのポートは追跡の対象外にする(rawテーブルのNOTRACK)、使用量のアラート。 - 数値の目安:デフォルトの上限はサーバーのメモリ量に応じて約6万〜26万件です。あふれるとカーネルログに「nf_conntrack: table full, dropping packet」が出力されます。 - グラフでは:上限で頭打ち(conntrackのエントリ数(nf_conntrack_count)) - 確認箇所:sysctlのnet.netfilter.nf_conntrack_count(現在のエントリ数)をnf_conntrack_maxと同じグラフに並べ、dmesgで「nf_conntrack: table full, dropping packet」を探す - 該当する場合:nf_conntrack_countがmaxで頭打ちになり、その時刻からカーネルログにtable fullが出ている - 該当しない場合:エントリ数がmaxに遠く及ばなければこの原因ではない。AWSインスタンス自体の接続追跡の上限は、「クラウドのPPS上限超過」にあるconntrack_allowance_exceededで確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · nf_conntrack_maxのデフォルト値はハッシュバケット数(nf_conntrack_buckets)と同じで、バケット数はメモリサイズで決まる - [net/netfilter/nf_conntrack_core.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_core.c?h=v6.12) · Linux kernel · デフォルトのサイズは、メモリが1GB超なら65,536で、4GB超(64ビット)なら262,144、満杯になると「nf_conntrack: table full, dropping packet」を記録して破棄 - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · rawテーブルのCT --notrackで接続追跡の対象外にする #### so-ports · サーバー間接続のエフェメラルポート枯渇 · Ephemeral port exhaustion (TIME_WAIT) ゲームサーバーがDBやほかのサーバーへの接続を短時間で頻繁に張っては切ると、切れた接続がしばらくポートを占有し、新しい接続を開けなくなります。 - なぜ → すると → 画面では:リクエストごとに新しい接続を開いて閉じる → 先に閉じた側が約60秒(Linux)の間ポートを占有し(TIME_WAIT)、使えるポートが尽きる → 内部リクエストが失敗し、保存の失敗・機能のエラー - 症状:不発・ロールバック, 接続不可・無限ロード / 要因:パケットロス - 誰に:サーバー全体, 特定の機能だけ / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:接続の再利用(コネクションプール)、リクエストごとに接続を開いて閉じないようにする。 - インフラチームの対応:ポート範囲の拡大(ip_local_port_range)、外向き接続でのTIME_WAITの再利用(Linuxのtcp_tw_reuse)の検討、TIME_WAIT数の監視。 - 数値の目安:Linuxのデフォルトのポート範囲(32768〜60999)は約2万8千個です。同じ接続先に1秒あたり470回を超えて新しく接続すると尽きます。Windowsはデフォルトのポートが約1万6千個(49152〜65535)で、TIME_WAITも長いため、さらに早く尽きます。 - グラフでは:上限で頭打ち(TIME_WAITソケット数、内部接続の失敗数) - 確認箇所:ss -tan state time-waitでTIME_WAITソケットを接続先アドレスごとに数え、ゲームサーバーのログからconnectの失敗(EADDRNOTAVAIL)を探す - 該当する場合:同じ接続先(DBなど)へのTIME_WAITがエフェメラルポートの範囲(デフォルトで約2万8千個)付近で頭打ちになり、connectがEADDRNOTAVAILで失敗している - 該当しない場合:TIME_WAITが少ないのに外部への接続だけが失敗するなら「クラウドのNATゲートウェイの接続・ポート上限」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:LinuxのTIME_WAITの60秒は、カーネルに固定された値です。名前の似たtcp_fin_timeoutを短くしても、TIME_WAITは短くなりません。 - 出典: - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · ip_local_port_rangeのデフォルトは32768〜60999、tcp_tw_reuse、tcp_fin_timeoutはFIN_WAIT_2状態を保持する時間 - [include/net/tcp.h (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_TIMEWAIT_LEN(60*HZ):TIME_WAITの約60秒はカーネルの定数 - [TCP/IP port exhaustion troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/tcp-ip-port-exhaustion-troubleshooting) · Microsoft · Windowsの動的ポートのデフォルトは49152〜65535、閉じた接続はデフォルトで4分間TIME_WAITとしてポートを保持 - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · 状態フィルターstate time-waitで、TIME_WAITソケットだけを絞り込んで表示 - [connect(2) — Linux manual page](https://man7.org/linux/man-pages/man2/connect.2.html) · Linux man-pages · EADDRNOTAVAIL:エフェメラルポートの範囲のポートがすべて使用中で、接続を開けない ### L8 ソケットとプロトコル(原因14件) #### sk-hol · TCPのHOLブロッキング · Head-of-line blocking TCPは順序を守るため、失われたパケットを1つ受け取り直すまで、後から届いたパケットをゲームに渡しません。 - なぜ → すると → 画面では:パケットが1つ消失 → 後続のパケットは届いているが、受信バッファで待機 → 止まった後に一気に解放されて早送り - 症状:フリーズ, 早送り / 要因:パケットロス, ストール - 誰に:自分だけ / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:リアルタイムの位置はUDP、どうしても必要なものだけ信頼性のある送信、ストリームを複数に分ける。クライアント:サーバーと同じ方式(UDP、チャネル分離)にネットワーク処理を変更。 - 数値の目安:パケットを1つ失うと最低でも往復時間+α、再送パケットまで失うと数百ms〜数秒止まります。 - グラフでは:途切れた後にまとめて到着(接続ごとの受信量、再送数) - 確認箇所:サーバー側のパケットキャプチャ(tcpdump・Wireshark)で、そのユーザーの接続の再送パケットとその前後の空白を確認し、サーバー全体はnstat -azでTcpRetransSegsの増加量を確認 - 該当する場合:止まった区間がパケット1つの再送から始まり、再送パケットが届いた直後にたまっていたデータが一気に処理される(受信量が0の状態から急増) - 該当しない場合:UDPで通信するゲームなら該当しない。再送がないのに止まるなら、サーバーのティック側(「ティックバジェット超過」)を確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · TCPは信頼性があり、順序を守るバイトストリームのサービス - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · 重複ACK 3つでロスを検知して高速再送、そうでなければ再送タイマーを待つ - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · 再送タイマーが満了するたびに2倍に延ばすバックオフ - [net/ipv4/proc.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatが表示するカウンター名:TcpグループのRetransSegs(再送したセグメント数) #### sk-rto · TCPのRTOと指数バックオフ · RTO and exponential backoff 再送がまた失敗するたびに待ち時間が2倍に延びるため、短い回線断が長い停止になります。 - なぜ → すると → 画面では:回線が一瞬切れて、再送も続けて失敗 → 次の試行までの時間が0.3 → 0.6 → 1.2 → 2.4秒のように2倍ずつ延びる(Ping 100msの場合) → 回線が切れたのは1秒なのに、ゲームは2秒以上止まる。さらに長く切れると最終的に切断 - 症状:フリーズ, 切断 / 要因:パケットロス, ストール - 誰に:自分だけ / いつ:ときどきランダムに, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:ハートビートに応答し、一定時間受信がなければ接続を先に整理(TCP_USER_TIMEOUTであきらめるまでの時間を短縮)、セッショントークンでセッションを引き継ぐ、信頼性UDP。クライアント:短い間隔でハートビートを送り、応答が途絶えたらTCPの再送を待たずにすぐ再接続。 - 数値の目安:LinuxのRTO(再送待ち時間)は「Ping+200ms」が最小で、接続を確立するときは1秒から始まります。デフォルト設定(tcp_retries2=15)では、再送が失敗し続けても約15分後にようやく接続をあきらめます。 - グラフでは:途切れた後にまとめて到着(接続ごとのRTO・backoff、RTOの満了回数) - 確認箇所:止まった接続をss -tiで確認してrto(再送待ちのms)とbackoff(連続満了回数)を、サーバー全体はnstat -azでTcpExtTCPTimeouts(再送タイマーの満了回数)の増加量を確認 - 該当する場合:止まった接続のbackoffが1以上で、rtoが秒単位まで大きくなっており、その時刻にTCPTimeoutsが増えている - 該当しない場合:再送が高速再送で済み、RTOの満了がなければ停止は短い。その場合は「TCPのHOLブロッキング」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · 最初のRTOは1秒、タイマーが満了するたびにRTOを2倍に(指数バックオフ) - [net/ipv4/tcp_input.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_input.c?h=v6.12) · Linux kernel · LinuxのRTO = 平滑化RTT + RTTの変動値で、変動値の下限がtcp_rto_min(200ms)のため、RTOはRTT+200ms以上 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_usのデフォルトは200ms、接続要求の最初のRTOは1秒、tcp_retries2=15ならあきらめるまで最低924.6秒(約15分) - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · -iのrto(再送タイマー、ms)とbackoff(指数バックオフの回数) - [net/ipv4/proc.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatが表示するカウンター名:TcpExtグループのTCPTimeouts - [net/ipv4/tcp_timer.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · 再送タイマーが満了するたびにTCPTimeoutsを加算し、backoffを1つ増やしてRTOを2倍に(最大値まで)延ばす #### sk-nagle · Nagleアルゴリズム+遅延ACK · Nagle + delayed ACK (TCP_NODELAY off) 小さなパケットをまとめて送るNagleアルゴリズムと、ACKを遅らせて送る遅延ACKがかみ合い、メッセージを分けて書き込むたびに40〜200msずつ遅延します。 - なぜ → すると → 画面では:TCP_NODELAYを有効にしないまま、小さなメッセージを分けて書き込む → 送信側はACKを待ち、受信側はACKを遅らせて送る → 回線のPingは低いのに、すべての操作が一様にもたつく入力遅延 - 症状:入力遅延 / 要因:遅延 - 誰に:サーバー全体, 自分だけ / いつ:常に, 特定の操作をしたとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:TCP_NODELAYを有効にする、1ティック分のメッセージをまとめて一度に書き込む、受信側の遅延ACKを無効にする方法(LinuxのTCP_QUICKACKは一時的にしか維持されない、WindowsはPCごとにレジストリを変更)はゲーム側で確実に制御できないので頼らない。クライアント:TCP_NODELAYを有効にする、1フレーム分のメッセージをまとめて一度に書き込む。 - 数値の目安:遅延ACKは、Linuxでは通常40ms(状況によって最大200ms)です。Windowsは以前のバージョンが200msで、最近のバージョンは40msです(Windows Server 2019のデフォルトテンプレートは40ms)。遅延ACKは受信側のOSが決めるため、サーバーがNagleを有効にしたままメッセージを分けて送ると、受信するPCによって40〜200msずつ遅延することがあります。 - グラフでは:最初から常に高い(操作の応答時間(ゲーム内のRTT)) - 確認箇所:サーバー側のパケットキャプチャ(tcpdump・Wireshark)でリクエストとレスポンスの間隔を確認し、サーバー・クライアントのコードがTCP_NODELAYを有効にしているかを確認 - 該当する場合:回線のPingは低いのに、小さなパケットの間に40ms(Windowsの以前のバージョンは200ms)前後の空白が繰り返し現れ、その空白は相手のACKが届いた直後に終わる。TCP_NODELAYを有効にすると消える - 該当しない場合:応答の間隔が回線のPingと同程度ならこの原因ではない。ゲームサーバーが応答を作るのが遅ければ、サーバーの処理側(「メッセージキューの滞留」) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · NagleはACKを受け取っていないデータがあると小さなデータをためておく、接続ごとに無効にできる必要がある、遅延ACKは0.5秒未満、両者がかみ合う問題 - [include/net/tcp.h (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · Linuxの遅延ACKの最小はTCP_DELACK_MIN(HZ/25 = 40ms)、最大はTCP_DELACK_MAX(HZ/5 = 200ms) - [TCP improvements in the Windows network stack (IETF 98 TCPM)](https://datatracker.ietf.org/meeting/98/materials/slides-98-tcpm-tcp-improvements-in-windows-01) · Microsoft · Windowsのデフォルトの遅延ACKタイムアウトを40msに変更(2017年発表) - [TCP Templates for Windows Server 2019 – How to tune your Windows Server Transports (Advanced users only 😉)](https://techcommunity.microsoft.com/blog/networkingblog/tcp-templates-for-windows-server-2019-8211-how-to-tune-your-windows-server-trans/339795) · Microsoft · Server 2019テンプレートのDelayedAckTimeout 40ms、MaxSynRetransmissions 2、InitialRto 3000ms - [Design issues - Sending small data segments over TCP with Winsock](https://learn.microsoft.com/en-us/previous-versions/troubleshoot/windows/win32/data-segment-tcp-winsock) · Microsoft · 以前のWindowsのTCPはデータを受け取ると200msの遅延ACKタイマーをセットし、Nagleはデフォルトで有効なので小さなパケットがACKを待つ、TCP_NODELAYで解決 #### sk-block-send · 遅いクライアントによるブロッキング送信 · Blocking send on a full socket 回線の遅い1人の送信バッファが満杯なのに、ブロッキング方式(バッファに空きができるまで呼び出しが戻らない送信)で送ると、サーバーのスレッドがその1人を待ちます。 - なぜ → すると → 画面では:遅いクライアントの送信バッファが満杯 → ブロッキング送信のため、サーバーのスレッドがバッファに空きができるまで待機 → そのスレッドが担当する全員がフリーズ・スローモーション - 症状:フリーズ, スローモーション / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:ときどきランダムに, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ノンブロッキング送信、クライアントごとの送信キューの上限、古い更新の破棄。 - グラフでは:不定期なスパイク(サーバーのティック時間、接続ごとのSend-Q) - 確認箇所:ss -tnで、接続ごとのSend-Q(ACKを受け取っていないか、まだ送れていないバイト)が送信バッファいっぱいまでたまった接続を探し、ティックが跳ねた瞬間のゲームサーバーのスレッドダンプ(スタック)で、send呼び出しで止まっているスレッドがあるかを確認 - 該当する場合:Send-Qが満杯の遅い接続があるとき、その接続を担当するスレッドがsendで止まっており、同じスレッドが担当する人たちだけが一緒に止まる - 該当しない場合:止まったスレッドがsend以外(ロック、DB呼び出し)で待っていれば「ロック競合」「ゲームスレッドの同期呼び出し」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [send(2) — Linux manual page](https://man7.org/linux/man-pages/man2/send.2.html) · Linux man-pages · 送信バッファに空きがなければsend()がブロックし、ノンブロッキングモードならEAGAINですぐに戻る - [send function (winsock2.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send) · Microsoft · Winsockも、バッファに空きがなければノンブロッキングモードでない限りsendがブロック - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · ssのRecv-Q・Send-Qの値:リッスンソケットではacceptを待っている接続数とbacklogの上限、接続済みソケットではアプリがまだ読んでいないバイト数とACKを受け取っていない送信バイト数 #### sk-slow-client · 遅いクライアント(slow consumer)の処理ポリシー · Slow-consumer policy 送るデータがたまり続けるクライアントに対して、サーバーが古い更新を破棄したり接続を切ったりします。 - なぜ → すると → 画面では:クライアントの回線が、サーバーが送る量に追いつかない → サーバーが古い更新を破棄するか、上限を超えたら接続を切断 → その人だけワープまたは切断 - 症状:ワープ, 切断 / 要因:パケットロス - 誰に:自分だけ / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:送る量を減らす(距離に応じた更新頻度)、品質を下げて送り続ける、カーネルにためておく量を減らす(LinuxのTCP_NOTSENT_LOWAT)。 - 数値の目安:送信バッファが256KBだと、30KB/sの回線では8秒以上分の送れていないデータがたまります。Linuxはこのバッファを自動で数MBまで大きくすることもあります。 - グラフでは:一部だけ高い(接続ごとのSend-Q、クライアントごとの破棄した更新の数) - 確認箇所:ゲームサーバーが記録するクライアントごとの送信キューの長さ・破棄した更新の数・切断理由を確認し、サーバーでss -tniを使ってその接続のSend-Qとcwndを合わせて確認 - 該当する場合:ワープしたり切断されたりした人の接続だけSend-Qがたまり続け、ゲームログにその人の更新の破棄や、送信キューの上限超過による切断が記録されている - 該当しない場合:Send-Qが空なのにワープするなら、サーバーの送信側の問題ではない。その人の回線のパケットロス(「無線区間のパケットロス」)か画面の補間側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_wmem:自動調整される送信バッファの最大値はデフォルト64KB〜4MB(メモリ量による)、tcp_notsent_lowat・TCP_NOTSENT_LOWATでまだ送っていないデータの量を制限 - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · ssのRecv-Q・Send-Qの値:リッスンソケットではacceptを待っている接続数とbacklogの上限、接続済みソケットではアプリがまだ読んでいないバイト数とACKを受け取っていない送信バイト数 #### sk-keepalive · keepaliveのデフォルト2時間 · TCP keepalive defaults 相手が終了の合図なしに消えると、TCPはかなり時間がたってから検知します。keepalive(アイドル接続が生きているかを確認するTCPの機能)はデフォルトで無効で、有効にしても2時間アイドル状態が続かないと確認を始めません。 - なぜ → すると → 画面では:クライアントが電源断や回線断で、終了の合図なしに消える → サーバーは接続が生きているとみなす(keepaliveのデフォルトは7,200秒。送信中のデータがあれば再送をあきらめるまで約15分) → キャラクターがゴーストとして残り、再接続すると「すでに接続中」エラー - 症状:接続不可・無限ロード, 表示されない・ゴースト / 要因:パケットロス - 誰に:自分だけ / いつ:しばらく放置した後, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発, インフラチーム・サーバーインフラ - ゲーム開発チームの対応:サーバー:ハートビートに応答し、一定時間受信がなければ接続を先に整理(TCP_KEEPIDLE・TCP_USER_TIMEOUTの調整)、再接続したらセッショントークンで既存のセッションを置き換えて引き継ぐ。クライアント:ゲームレベルのハートビートを数秒〜数十秒間隔(最も短いアイドルタイムアウトの半分以下)で送る、切れたら自動で再接続。 - インフラチームの対応:コードで個別に設定していないソケットのために、カーネルのデフォルト値(tcp_keepalive_timeなど)を下げる(SO_KEEPALIVEを有効にしたソケットにのみ適用)。 - 数値の目安:Linuxのデフォルトでは、7,200秒アイドル状態が続くと確認を始め、75秒間隔でプローブを9回送り、最後まで応答がなければ接続を切ります。合計で約2時間11分です。Windowsもデフォルトでは、2時間アイドル状態が続かないと確認を始めません。 - グラフでは:一部だけ高い(接続ごとの最終受信からの経過時間) - 確認箇所:ss -tnoiで接続ごとのlastrcv(最終受信からの経過ms)とkeepaliveタイマー(timer:(keepalive,…))を確認し、ゲームサーバーが「すでに接続中」で拒否した記録と突き合わせる - 該当する場合:lastrcvが数分〜数時間のESTABLISHED接続が残っており、そのアカウントの再接続が「すでに接続中」で拒否されている - 該当しない場合:長時間無通信の接続がないのに「すでに接続中」が出るなら、ゲームサーバーのセッション整理のコード側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · 7,200秒アイドルの後に75秒間隔でプローブ9回(約11分追加)、SO_KEEPALIVEを有効にしたソケットにのみ適用、TCP_KEEPIDLE・TCP_USER_TIMEOUT - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · keepaliveはデフォルトで無効でなければならず、アイドル間隔のデフォルト値は2時間以上 - [SO_KEEPALIVE socket option](https://learn.microsoft.com/en-us/windows/win32/winsock/so-keepalive) · Microsoft · WindowsのTCP keepaliveのデフォルトタイムアウトは2時間 - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · -iのlastrcv(最終受信からの経過ms)、-oのtimer:(keepalive,…) #### sk-fragment · UDPパケットのIPフラグメンテーション · IP fragmentation of large UDP MTU(一度に送れるサイズ)を超えるUDPパケットはIP層でフラグメント化され、フラグメントを1つ失うだけでパケット全体が破棄されます。 - なぜ → すると → 画面では:人の多い場所のスナップショットが1,500バイトを超える → 複数のフラグメントに分割して送信、1つでも失うと全体を破棄 → 大きなパケットほどロス率が数倍になる。混雑した場所でだけワープ - 症状:ワープ / 要因:パケットロス - 誰に:特定の場所・チャンネル, 特定の地域・ISP / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:パケットを1,200バイト以下に自前で分割、変更分だけを送る。 - 数値の目安:ロス率2%の回線では、4つのフラグメントに分かれたパケットの約8%が失われます。フラグメント化されたパケットを一律に破棄するファイアウォールや通信事業者もあり、その場合ユーザーは大きなパケットを1つも受け取れません。 - グラフでは:人数・負荷に連動して上昇(IPフラグメンテーションの数(IpFragCreates)、スナップショットのサイズ) - 確認箇所:サーバーでnstat -azのIpFragCreates(送信時に作ったフラグメント数)の増加量を確認し、受信側ではIpReasmFails(再構築の失敗数)を確認。ゲームサーバーのログやパケットキャプチャでUDPパケットのサイズ分布を確認 - 該当する場合:人が集中する場所でIpFragCreatesが増え、1,500バイトを超えるUDPパケットがあり、そのときワープの報告が増えている - 該当しない場合:IpFragCreatesが増えなければ、サーバーの送信側ではフラグメンテーションは起きていない - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · フラグメントを1つ失うと再構築できずパケット全体を失う、UDPアプリはIPフラグメンテーションを避けるべき - [RFC 8900: IP Fragmentation Considered Fragile](https://www.rfc-editor.org/rfc/rfc8900) · IETF · ファイアウォールや一部のネットワークがIPフラグメントを破棄する事例 - [net/ipv4/proc.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatが表示するカウンター名:IpグループのFragCreates(作ったフラグメント数)・ReasmFails(再構築の失敗数) #### sk-reliable-udp · 信頼性UDPの再送設定 · Reliable-UDP tuning (KCP, ENet…) UDPの上に独自に作った再送ルールが保守的すぎると復旧が遅れ、攻撃的すぎると回線をさらに詰まらせます。 - なぜ → すると → 画面では:再送の間隔・回数・ウィンドウサイズの設定が回線に合っていない → 復旧の遅れ、または重複送信による輻輳の悪化 → スキル不発、早送り、輻輳時にさらにひどいラグ - 症状:不発・ロールバック, 早送り / 要因:パケットロス, 遅延 - 誰に:自分だけ / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:測定した往復時間に基づく再送、重要度ごとのチャネル分離。クライアント:サーバーと同じ再送・チャネル設定を適用。 - グラフでは:不定期なスパイク(信頼性UDPの再送率、ゲーム内のRTT) - 確認箇所:使っているライブラリが接続ごとに持つ統計(再送数、推定往復時間、再送待ち時間)をサーバー・クライアントで記録し、同じユーザーの実際の回線のロス率(mtrで測った値)と比較 - 該当する場合:再送率が実際の回線のロス率より数倍高ければ攻撃的すぎる設定、再送待ち時間が測定した往復時間の数倍なら保守的すぎる設定 - 該当しない場合:再送率が回線のロス率と同程度で、待ち時間が往復時間に見合っていれば設定の問題ではない。回線のパケットロスそのものを確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · 再送は輻輳を悪化させうるので輻輳制御の対象、往復時間は複数の測定の平均(EWMA)で推定、初期値1秒、タイマー満了時は送信レートを下げる #### sk-slowstart · アイドル後のスロースタート · Slow start after idle TCPはしばらくアイドル状態が続くと輻輳ウィンドウ(一度に送れる量)を再び縮めるため、急に大きなデータを送るときに何回かに分けて送ります。 - なぜ → すると → 画面では:アイドル状態だった接続で、街への入場など大きなデータを送る → 輻輳ウィンドウが縮んでいるため、複数の往復に分けて送信 → 入場直後、周りのキャラクターやNPCが往復数回分遅れて表示される(遠いサーバーほど目立つ) - 症状:入力遅延, 表示されない・ゴースト / 要因:遅延 - 誰に:自分だけ / いつ:移動中・マップ切り替え時, しばらく放置した後 - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:入場時のデータを減らす(どうしても必要なものから先に送る)。 - インフラチームの対応:tcp_slow_start_after_idleを無効にする(Linux、サーバー全体の設定)。 - 数値の目安:RTOより長くアイドル状態が続くと輻輳ウィンドウが縮み始め、長くアイドルだった場合は約14KB(パケット10個)まで下がります。そうなると100KBは一度に送れず、3往復に分けて送ることになります。 - グラフでは:一部だけ高い(入場直後の送信時間(RTTが長いユーザー)) - 確認箇所:sysctl net.ipv4.tcp_slow_start_after_idleの値を確認し、アイドル後にエリアへ入場した瞬間、その接続のss -tiでcwnd(輻輳ウィンドウ)が小さくなっているかを確認 - 該当する場合:設定が1(デフォルト)で、アイドル後の入場の瞬間にcwndが10前後まで縮み、送信が複数の往復に分かれる。RTTが長いユーザーほど遅れて表示され、0に変えると解消する - 該当しない場合:cwndが大きいまま維持されているのに遅れて表示されるなら、サーバー側の入場処理(「密集エリア進入時のスポーン集中」) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_slow_start_after_idleはデフォルトで有効、RTOの間アイドルなら輻輳ウィンドウを縮める(RFC 2861の方式) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · RTOより長くデータを送っていなければ、輻輳ウィンドウをリスタートウィンドウmin(IW, cwnd)以下に縮めてスロースタート - [RFC 6928: Increasing TCP's Initial Window](https://www.rfc-editor.org/rfc/rfc6928) · IETF · 初期ウィンドウは10セグメント、最大14,600バイト - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · -iのcwnd(輻輳ウィンドウ)・ssthresh(スロースタートのしきい値) #### sk-congestion · 輻輳制御による送信量の急減 · Congestion control backoff TCPはパケットロスを輻輳のシグナルとみなし、送信速度を30〜50%落とします。Wi-Fiでのパケットロスにも同じように反応します。 - なぜ → すると → 画面では:送る量が多いときに、Wi-Fiや回線でわずかなパケットロスが発生 → TCPが送信速度を大きく落とし、ゆっくり回復(Linux・WindowsのデフォルトであるCUBICは30%落とす) → 人の多い場所で更新が遅れて早送り・入力遅延 - 症状:早送り, 入力遅延 / 要因:遅延, ストール - 誰に:自分だけ / いつ:人が集中したとき - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:送る量を減らす(関心領域、変更分だけ)、一度にまとめて送らないよう分けて送る。 - インフラチームの対応:BBRのような輻輳制御に切り替える(tcp_congestion_control)。 - グラフでは:徐々に上昇して急落(接続ごとの輻輳ウィンドウ(cwnd)・送信レート) - 確認箇所:更新が遅れたユーザーの接続をss -tiで何度か取得し、cwnd・ssthreshの変化と輻輳制御アルゴリズムの名前(cubic・bbr)を確認しながら、Send-Qがたまっていくかも合わせて確認 - 該当する場合:パケットロスの後にcwndが大きく縮んでからゆっくり上がることを繰り返し、縮んでいる間にSend-Qがたまり、早送り・入力遅延の報告時刻と重なる - 該当しない場合:cwndに余裕があるのに遅れるなら、受信側のウィンドウ(「ゼロウィンドウ(再送のように見える停止)」)かサーバーの送信側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 9438: CUBIC for Fast and Long-Distance Networks](https://www.rfc-editor.org/rfc/rfc9438) · IETF · CUBICはパケットロス時にウィンドウを0.7倍(30%減)、Renoは0.5倍、CUBICがLinux・Windows・Appleのデフォルト - [TCP BBR congestion control comes to GCP – your Internet just got faster](https://cloud.google.com/blog/products/networking/tcp-bbr-congestion-control-comes-to-gcp-your-internet-just-got-faster) · Google Cloud · ロスベースの輻輳制御は、輻輳が原因でないパケットロスでも送信レートを大きく下げる、BBRは配送レートとRTTで判断 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_congestion_controlで、新しい接続の輻輳制御アルゴリズムを選択 - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · -iのcwnd・ssthreshと輻輳制御アルゴリズムの名前 - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · ssのRecv-Q・Send-Qの値:リッスンソケットではacceptを待っている接続数とbacklogの上限、接続済みソケットではアプリがまだ読んでいないバイト数とACKを受け取っていない送信バイト数 #### sk-linger · RSTによる強制終了で最後のデータが消失 · SO_LINGER, abrupt RST サーバーが接続を急に切ると、最後に送った案内や保存完了の通知が失われます。 - なぜ → すると → 画面では:サーバーが接続を強制終了(RST)で閉じる。SO_LINGERを0秒にしたり、受信したデータを読み切らずに閉じたりすると起きる → まだ送信中だったキックの理由や最後のデータが破棄される → 理由の分からない「不明なエラーにより接続が切断されました」 - 症状:切断 / 要因:パケットロス - 誰に:自分だけ / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:理由を送った後に送信方向だけを先に閉じ(shutdown)、相手が閉じるまで受信したデータを最後まで読んでから閉じる、SO_LINGERの0秒を避ける。 - グラフでは:不定期なスパイク(RSTで終わった接続の数) - 確認箇所:nstat -azのTcpExtTCPAbortOnData(送るデータが残ったままRSTで閉じた、SO_LINGER 0秒)・TcpExtTCPAbortOnClose(読んでいないデータが残ったまま閉じた)の増加量を確認し、切断の瞬間にサーバー側のパケットキャプチャでFINの代わりにRSTが出ているかを確認 - 該当する場合:「不明なエラーにより接続が切断されました」の報告時刻にサーバーがRSTを送っており、AbortOnData・AbortOnCloseが増えている - 該当しない場合:サーバーがFINで正常に終了しているのに理由が表示されなければ、クライアントの終了処理側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [closesocket function (winsock.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-closesocket) · Microsoft · SO_LINGERを有効にして時間を0にすると、接続を即座にリセットする強制終了になり、送れなかったデータは失われる - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · 受信したデータが残ったまま閉じると、データの消失を知らせるためにRSTを送る - [Graceful Shutdown, Linger Options, and Socket Closure](https://learn.microsoft.com/en-us/windows/win32/winsock/graceful-shutdown-linger-options-and-socket-closure-2) · Microsoft · shutdownで送信だけを先に閉じ、相手の終了通知を受け取ってからソケットを閉じる手順 - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPAbortOnData:SO_LINGER 0秒などで、送るデータが残ったままRSTで閉じた、TcpExtTCPAbortOnClose:読んでいないデータが残ったまま閉じてRSTを送った #### sk-blocking-io · ブロッキングI/O構造 · Blocking I/O model 1つのソケットを待っている間、スレッドがほかの処理をできない構造では、人が増えるほど全体が遅くなります。 - なぜ → すると → 画面では:接続ごとに読み書きを待つ方式 → 1つの接続の遅延が、同じスレッドのほかの接続に波及 → 同時接続数が増えるほど、全体がスローモーション・入力遅延 - 症状:スローモーション, 入力遅延 / 要因:ストール - 誰に:サーバー全体 / いつ:人が集中したとき, 夜のピーク時間帯 - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:epoll・IOCP・io_uringベースの非同期I/Oに移行。 - グラフでは:人数・負荷に連動して上昇(応答時間、スレッド数) - 確認箇所:pidstat -w -tでゲームサーバーのスレッド数とスレッドごとの自発的コンテキストスイッチ(cswch/s。資源を待って止まった回数)を確認し、同時接続数に応じた応答時間と比較 - 該当する場合:同時接続数が増えるほど応答時間が急激に上がり、接続数に比例して増えたスレッドのほとんどが自発的スイッチばかり多く、CPUはほとんど使っていない(ソケット待ち) - 該当しない場合:スレッドが待ちなしでCPUを使い続けていれば、計算の過負荷(「ティックバジェット超過」) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [epoll(7) — Linux manual page](https://man7.org/linux/man-pages/man7/epoll.7.html) · Linux man-pages · 多数のfdを一度に監視できるよう、スケールするI/Oイベント通知 - [I/O Completion Ports](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports) · Microsoft · 多数の非同期I/Oを、あらかじめ作ったスレッドプールで処理するWindowsの方式 - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -wのcswch/s:資源を待って自ら停止した自発的コンテキストスイッチ、-tでスレッドごと #### sk-reuseport · SO_REUSEPORTの振り分けの偏り · SO_REUSEPORT imbalance, stuck worker 同じポートを複数のプロセスで分担して受け付けると、カーネルは接続ごとにアドレスのハッシュで担当プロセスを決め、その後は変更しません。担当プロセスが1つ止まると、そこに割り当てられた人だけが待たされます。 - なぜ → すると → 画面では:ゲートウェイ・ログインサーバーがSO_REUSEPORTで複数のプロセスを起動 → 1つのプロセスがGCや過負荷で止まっても、そこに割り当てられた新しい接続とUDPパケットはほかのプロセスに回されない → 一部の人だけ接続不可・フリーズ。プロセス数が変わる再起動時には、一部のUDPセッションが切れる - 症状:接続不可・無限ロード, フリーズ, 切断 / 要因:ストール, パケットロス - 誰に:自分だけ, サーバー全体 / いつ:接続直後・メンテ明け, ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:受信スレッドは絶対に止まらないようにする、再起動時にセッションを引き継ぐ手順を実装。 - インフラチームの対応:プロセスごとの接続待ちキューの監視(ssのRecv-Q)、デプロイでプロセス数を変えるときはセッション引き継ぎの手順に従うようにする。 - グラフでは:一部だけ高い(リッスンソケットごとの接続待ちキュー(Recv-Q)) - 確認箇所:ss -ltnpで、同じポートのリッスンソケットごとのRecv-Q(acceptを待っている接続数)と担当プロセスを確認し、プロセスごとのスループットを比較 - 該当する場合:同じポートの複数のソケットのうち1つだけRecv-Qがたまり続け、そのプロセスが止まっているかスループットが0に近い - 該当しない場合:すべてのソケットのRecv-Qが均等にたまっていれば、全体の過負荷(「接続待ちキュー(backlog)のあふれ」) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · SO_REUSEPORTで複数のソケットが同じアドレスにbindし、TCP接続とUDPパケットを分担して受け取る - [net/core/sock_reuseport.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/sock_reuseport.c?h=v6.12) · Linux kernel · BPFプログラムがなければ、パケットのハッシュ値をグループのソケット数で割って担当ソケットを選ぶ - [Why does one NGINX worker take all the load?](https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/) · Cloudflare · SO_REUSEPORTは単純なハッシュでワーカーごとにキューを分けるため、ワーカーが1つ詰まると、そのキューにたまった接続がすべて止まる - [net/ipv4/tcp_diag.c (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · ssのRecv-Q・Send-Qの値:リッスンソケットではacceptを待っている接続数とbacklogの上限、接続済みソケットではアプリがまだ読んでいないバイト数とACKを受け取っていない送信バイト数 #### sk-udp-connreset · WindowsのUDPソケットのWSAECONNRESETエラー · WSAECONNRESET on a Windows UDP socket Windowsサーバーがすでに去ったクライアントにUDPを送ると、「ポート到達不能」(ICMP)の通知が返ってきます。その通知のせいで次の受信呼び出しがエラーで終わりますが、サーバーのコードがこのエラーをソケット自体の故障として扱うと、そのソケットを使っている全員が影響を受けます。 - なぜ → すると → 画面では:出ていったばかりのクライアントのアドレスにUDPを送り続け、「ポート到達不能」(ICMP)の通知が返ってくる → Windowsが次の受信呼び出しをWSAECONNRESET(10054)エラーで終わらせ、サーバーのコードが受信を止めるかソケットを閉じる → そのソケットを使っていた全員が一斉にフリーズ・切断 - 症状:切断, フリーズ / 要因:パケットロス, ストール - 誰に:サーバー全体 / いつ:ときどきランダムに, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:WSAIoctlでSIO_UDP_CONNRESETを無効にしてこの通知を受け取らない、受信エラーが起きてもログだけ残して受信を続ける。 - グラフでは:接続が一斉に切れる(接続数、受信エラーのログ) - 確認箇所:ゲームサーバーのログからUDP受信のエラーコード(WSAECONNRESET、10054)と、受信ループが止まったりソケットを閉じたりした記録を探し、サーバー側のパケットキャプチャで直前にICMP Port Unreachableが届いたかを確認 - 該当する場合:接続が一斉に切れる直前にWSAECONNRESETの受信エラーが記録され、その前に、出ていったばかりのクライアントのアドレスからICMP Port Unreachableが届いている - 該当しない場合:Linuxサーバーであるか、コードでSIO_UDP_CONNRESETを無効にしていれば該当しない - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Winsock IOCTLs](https://learn.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls) · Microsoft · SIO_UDP_CONNRESETで、UDPの「ポート到達不能」(PORT_UNREACHABLE)通知の報告を有効・無効にする - [recvfrom function (winsock.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom) · Microsoft · UDPソケットのWSAECONNRESETは、以前の送信がICMP Port Unreachableを受け取ったという意味 - [Windows Sockets Error Codes](https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2) · Microsoft · WSAECONNRESETのエラー番号は10054 ### L9 サーバーのゲームプロセス(原因18件) #### sp-tick-overrun · ティックバジェット超過 · Tick overrun 1ティックの処理がバジェットを超えるとサーバーのティック周期が延び、そのエリア全体がゆっくり進んだりカクついたりします。 - なぜ → すると → 画面では:1ティック(例:50ms)で処理する仕事がバジェットを超える → 1秒に20回計算するはずのゲーム状態を8回しか計算できない → そのエリア全体がスローモーション(サーバーの設計によってはカクつき)、スキルの反応が遅い - 症状:スローモーション, 入力遅延, カクつき / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:人が集中したとき, 夜のピーク時間帯 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:重い計算を減らす、ティックを複数のスレッドに分割、人数の分散(チャンネル)、ティックの処理時間をメトリクスとして記録。 - インフラチームの対応:ティック時間とコアごとのCPU使用率を監視・アラートに追加、シングルコア性能(クロック)の高いCPU・インスタンスを検討。 - 数値の目安:20ティックのサーバーのバジェットは50ms、30ティックは33ms、60ティックは16.7msです。急な集中に備えて、普段はバジェットの半分程度しか使わないよう余裕を持たせるほうが安全です。 - グラフでは:人数・負荷に連動して上昇(サーバーのティック時間、ゾーン・チャンネルごとの人数、ゲームスレッドのCPU) - 確認箇所:サーバーが記録するティック処理時間(p99)・ティック超過回数を、ゾーン・チャンネルごとの人数と同じグラフに並べて確認。ティックのメトリクスがなければ、pidstat -t 1でゲームスレッド1本のCPU使用率 - 該当する場合:人数が集中した時刻にティック時間がバジェット(20ティックなら50ms)を超え、その間ゲームスレッドのCPU使用率が100%近くに張り付いている - 該当しない場合:ティックが超過しているのにゲームスレッドのCPUが低ければ、待ち側の原因(GC停止、ロック、同期呼び出し)。bccのrunqlatでランキューの待ち時間が長ければ、スレッドにCPUが割り当てられていないので、CPU不足・スレッド過多の側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:ティックが遅れたときの見え方は、サーバーの設計によって異なります。ティックごとにゲーム状態を決まった時間(例:50ms)だけ進めるサーバーでは、ゲーム内の時間そのものが遅くなってスローモーションになります。実際に経過した時間の分だけ一度に動かすサーバーでは、ゲームの進行速度は保たれますが、パケットがまばらになり一度に大きく動くため、カクつき・ワープに見えます。どちらの場合も入力の反応は遅れます。ゲームスレッド1つがサーバー全体を担当していればサーバー全体が、エリアごとにスレッドを分けていればそのエリアが遅くなります。EVE Onlineのように、大規模な戦闘でわざとゲーム内の時間を最大10倍まで遅くして(Time Dilation)計算を追いつかせるゲームもあります。 - 実際の事例:eve-hedgp-2014 - 出典: - [VALORANT's 128-Tick Servers](https://www.riotgames.com/en/news/valorants-128-tick-servers) · Riot Games · 128ティックのサーバーは1フレームを7.8125ms以内に終える必要がある、サーバーのフレーム時間をサブシステムごとに計測し、バジェットを配分して管理 - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · EVE Onlineは過負荷時にTime Dilationでゲーム内の時間を遅くし、下限は10%(10倍遅い)、普段のノードのCPUは80%未満 - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · 固定間隔のシミュレーションが遅れると、追いつくためのステップをまとめて実行し、上限を超えた時間は捨てるため、ゲーム内の時間が実際より遅く流れる - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -tで、プロセスに属するスレッドごとの統計(CPU使用率など)も表示 - [Demonstrations of runqlat, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/runqlat_example.txt) · IO Visor · スケジューラーのランキュー待ち時間(タスクにCPUが割り当てられるまで待った時間)をヒストグラムで表示 #### sp-aoi · 視界(AOI)計算の急増(N²) · Area-of-interest explosion 誰が誰を見られるかを全員同士で比較すると、人数が10倍になったとき計算は100倍になります。 - なぜ → すると → 画面では:すべてのキャラクター同士で距離を比較しているか、グリッドに分割していても1つのセルの周辺に数百人が集中 → 100人なら約1万回、1,000人なら約100万回の比較 → ワールドボスや攻城戦のように人が集中した場所でティック時間が急増し、スローモーション・カクつき - 症状:スローモーション, カクつき / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:グリッド・区域に分けて近くだけを比較、遠くの対象はまれに更新、1人が見る人数に上限。 - 数値の目安:距離の比較と、見える・見えないリストの更新を合わせて2人1組あたり0.1µs(1千万分の1秒)とすると、1,000人(約100万組)なら1ティックに100msかかります。20ティックのバジェット(50ms)の2倍です。 - グラフでは:人数・負荷に連動して上昇(サーバーのティック時間、1か所に集まった人数) - 確認箇所:ゾーン・チャンネルごとの人数とティック時間を同じグラフに並べ、ティック内で視界計算に使った時間を個別に計測した値を確認。個別の計測値がなければ、perf top -pでゲームプロセスの関数ごとのCPU比率 - 該当する場合:1か所に集まった人数が2倍になるとティック時間が4倍近くに増え、視界・距離計算の関数がCPU時間の大半を占めている - 該当しない場合:ティック時間が人数に比例して増えるか、送信・シリアライズの関数の比率が大きければ、ブロードキャストの急増かシリアライズ・圧縮のコスト側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Comparing Interest Management Algorithms for Massively Multiplayer Games](https://www.sable.mcgill.ca/~clump/papers/boulanger-06-comparing.pdf) · ACM · NetGames 2006の論文(著者公開版)。全ペアの距離を測る方式は人数が増えると処理しきれず、正方形のグリッドに分ければ周囲9セルだけを確認 - [Replication Graph in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/replication-graph-in-unreal-engine) · Epic Games · アクターごとにすべての接続を調べる基本の方式は、人数・アクターが多いとサーバーCPUのボトルネックになる、MMORPGなどはワールドをグリッドに分けてセルごとのリストを再利用 - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · 実行中のプロセス(-p)やスレッド(-t)のCPU使用比率を関数(シンボル)ごとにリアルタイム表示 #### sp-broadcast · ブロードキャストの急増 · Broadcast fan-out (N×N) 1人の動きを、その人が見えている全員に送ると、集まった人数の2乗に比例する数の更新を送ることになります。 - なぜ → すると → 画面では:1人の変化を、それが見える全員に送信 → 1,000人が互いを見ていると、ティックごとに100万個の更新 → 送信キューと帯域幅が飽和して遅延・パケットロス(入力遅延・早送り・ワープ) - 症状:入力遅延, ワープ, 早送り / 要因:遅延, パケットロス - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:距離・重要度に応じて更新頻度を下げる(近くの敵は毎ティック、遠くの人は1秒に数回)、1人に送る量に上限を設けて重要なものから詰める、複数の更新を1つのパケットにまとめる、表示人数の上限。 - インフラチームの対応:サーバーごとの送信帯域幅・毎秒パケット数をNIC・インスタンスのネットワーク上限と比べてアラート、大規模イベントの前に余裕を確認。 - 数値の目安:1,000人 × 1,000人 × 20ティック = 毎秒2,000万個。1個40バイトならサーバー全体で約6.4Gbps、受信する1人あたり約6.4Mbps。見える人数を150人に制限すると全体で約1Gbps、1人あたり約1Mbps。 - グラフでは:人数・負荷に連動して上昇(サーバーの送信パケット・バイト、1か所に集まった人数) - 確認箇所:sar -n DEV 1のtxpck/s・txkB/s(サーバーのNICが毎秒送ったパケット数・KB)を人数のグラフと並べて確認。クラウドのインスタンスではethtool -Sの上限超過カウンター(AWS ENAはbw_out_allowance_exceeded・pps_allowance_exceeded) - 該当する場合:集まった人数が増えると送信パケット・バイトが人数よりも急激に(2乗に近く)増え、上限に達した時刻から上限超過カウンターや送信の破棄が増加 - 該当しない場合:送信量は変わらないのにティック時間だけが増えるなら、視界計算・ゲームロジック側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 実際の事例:eve-hedgp-2014 - 出典: - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · n人の行動をn人が見なければならないO(n²)の送信が、大規模な艦隊戦で避けられない制約要因 - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · 接続の帯域幅が飽和すると、アクターごとに優先度(距離・視線・最後の送信からの経過時間)を付け、重要なものから帯域幅を配分 - [Detailed Actor Replication Flow in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/detailed-actor-replication-flow-in-unreal-engine) · Epic Games · NetUpdateFrequencyでアクターごとの更新頻度を決め、優先度順に送って接続が飽和したら残りは次のティックに回す - [sar(1) — Linux manual page](https://man7.org/linux/man-pages/man1/sar.1.html) · sysstat · sar -n DEVのrxpck/s・txpck/s(毎秒の受信・送信パケット数)、rxkB/s・txkB/s(毎秒の受信・送信KB) - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · bw_out_allowance_exceeded(送信帯域幅の上限超過)・pps_allowance_exceeded(PPS上限超過)で、キューにたまったか破棄されたパケットの数 #### sp-hotzone · シングルスレッドのエリア過負荷(ホットスポット) · Single-threaded hot zone エリアごとに1つのスレッドが担当する構造では、1か所に人が集中すると、そのコア1つだけが100%になります。 - なぜ → すると → 画面では:1つのエリア(チャンネル)を1つのスレッドが担当 → 人が1か所に集中するとそのコアだけが飽和し、ほかのコアには余裕がある → そのエリアだけラグが出て、ほかのエリアは問題ない - 症状:スローモーション, 入力遅延 / 要因:ストール - 誰に:特定の場所・チャンネル / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:チャンネルの分散、エリア内部の並列化、人数の上限。 - インフラチームの対応:コアごとのCPU使用率を監視・アラートに追加(サーバー全体の平均ではコア1つの飽和が埋もれる)。 - 数値の目安:16コアのサーバーでコア1つが100%でも、サーバー全体のCPU使用率は約6%に見えます。コアごとの使用率を確認しないと見つけられません。 - グラフでは:人数・負荷に連動して上昇(コアごとのCPU使用率、スレッドごとのCPU) - 確認箇所:mpstat -P ALL 1でコアごとの使用率を、pidstat -t 1でゲームプロセスのスレッドごとのCPUを確認し、最も忙しいスレッドが担当するゾーンの人数と比較 - 該当する場合:サーバー全体のCPUは低いのに、スレッド1つ(コア1つ)だけが100%近くに張り付いており、その時刻にそのスレッドが担当するゾーンに人が集中している - 該当しない場合:複数のコアが均等に高ければ、サーバー全体の過負荷。1つのコアの%soft(受信処理)だけが高ければ、NIC割り込みの単一コア集中の側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:ほかのエリアが問題ないのは、エリアごとにティックを個別に回している場合の話です。複数のエリアのスレッドが毎ティック互いを待ってから一緒に次のティックへ進む構造なら、最も忙しいエリア1つがサーバー全体のティックを遅らせます。 - 実際の事例:eve-hedgp-2014 - 出典: - [Time Dilation – How’s That Going?](https://www.eveonline.com/news/view/time-dilation-hows-that-going) · CCP Games · EVE OnlineのTime Dilationはノード単位のため、同じノードに載っている遠くの星系まで遅くなる、大規模な戦闘は星系を4つだけ載せた強化ノードで処理 - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · プロセッサーごとの使用率と全体の平均を分けて表示(-P ALL)、%softはソフトウェア割り込みの処理に使った時間の割合 - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -tで、プロセスに属するスレッドごとの統計(CPU使用率など)も表示 #### sp-lock · ロック競合 · Lock contention 複数のスレッドが同じデータを使うために1つのロックを待つと、スレッドを増やしても一度に1つずつしか実行されません。 - なぜ → すると → 画面では:取引所・ギルド倉庫のような共有データを、複数のスレッドが同時に使用 → ロックを取ったスレッドが終わるまで、ほかのスレッドは待機 → 特定の機能だけ遅い、ひどい場合は全体のティックが遅延 - 症状:入力遅延, フリーズ / 要因:ストール - 誰に:特定の機能だけ, サーバー全体 / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ロックを細かく分ける、ロック内で行う処理を減らす、メッセージベースの構造(データごとに担当スレッドを決め、ほかのスレッドはメッセージでリクエストだけを送る)。 - 数値の目安:ロック内の処理が全体の20%なら、スレッドをいくら増やしてもスループットは1スレッドのときの最大5倍、40%なら2.5倍で頭打ちになります。 - グラフでは:人数・負荷に連動して上昇(リクエストの処理時間、スレッドごとのCPU・コンテキストスイッチ) - 確認箇所:pidstat -w -t 1でスレッドごとの自発的コンテキストスイッチ(cswch/s。資源を待って止まった回数)を、bccのoffcputime -pでスレッドがCPUを離れてどこで待っているか(コールスタックごとの待ち時間)を確認。.NETはdotnet-countersのロック競合数(.NET 9以降はdotnet.monitor.lock_contentions、8以前はMonitor Lock Contention Count) - 該当する場合:負荷が増えてもCPU使用率は低いままなのに処理時間が延び、待ち時間の大半がロックを取ろうとするコールスタックに集中し、ロック競合数も一緒に上がる - 該当しない場合:CPUが飽和していれば計算量の問題(ティックバジェット超過、シングルスレッドのエリア過負荷)。待っている箇所がDB・ファイルの呼び出しなら、ゲームスレッドの同期呼び出しの側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:複数のスレッドがゲームデータを共同で書き換える構造で起きます。エリア・機能ごとに1つのスレッドが担当し、メッセージだけでやり取りする構造ではロックはほとんど要りませんが、その代わり1つのスレッドに仕事が集中する問題(シングルスレッドのエリア過負荷)に注意が必要です。ゲームスレッドが、遅い保存処理の取っているロックを待つと、そのティック全体が止まります。 - 実際の事例:roblox-2021 - 出典: - [Amdahl's Law in the Multicore Era](https://research.cs.wisc.edu/multifacet/papers/ieeecomputer08_amdahl_multicore.pdf) · IEEE · IEEE Computer 2008の論文(著者公開版)。並列化できない割合が1−fなら、コアをいくら増やしても高速化は1/(1−f)を超えない(アムダールの法則) - [Request scheduling](https://learn.microsoft.com/en-us/dotnet/orleans/grains/request-scheduling) · Microsoft · Orleansのグレイン(アクター)は、リクエストを1つずつ最後まで処理するシングルスレッドの実行モデルなので状態を同時に書き換えない、互いの応答を待つとデッドロックが起こり得る - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -wのcswch/sは資源を待って止まった自発的コンテキストスイッチの回数、-tでスレッドごとに表示 - [Demonstrations of offcputime, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/offcputime_example.txt) · IO Visor · スレッドが止まってCPUを離れていた時間(off-CPU)をコールスタックごとに集計、-pでプロセスを指定 - [Well-known EventCounters in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/available-counters) · Microsoft · Monitor Lock Contention Count(monitor-lock-contention-count):モニターロックを取得しようとして競合が発生した回数 - [.NET runtime metrics](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-runtime) · .NET · .NET 9以降のdotnet.monitor.lock_contentions:プロセス開始後、モニターロックを取得しようとして競合が発生した回数 #### sp-deadlock · デッドロック · Deadlock 2つのスレッドが互いに相手の取ったロックを待つと、永遠に止まったままになります。 - なぜ → すると → 画面では:スレッドAはロック1を取ったままロック2を、Bはロック2を取ったままロック1を待つ → 両方とも永遠に止まり、関連するスレッドも次々に止まる → サーバー全体が停止し、ウォッチドッグによる再起動で全員が切断 - 症状:フリーズ, 切断 / 要因:ストール - 誰に:サーバー全体, 特定の機能だけ / いつ:ときどきランダムに, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ロックを取る順序のルール、タイムアウト付きのロック、ウォッチドッグと停止した瞬間のスレッドダンプの記録。 - グラフでは:接続が一斉に切れる(接続数、サーバーの送信量) - 確認箇所:止まっている間に全スレッドのコールスタックを取得。JVMはjstack(デッドロックを自動で検出して表示)、.NETはdotnet-stack、ネイティブのサーバーはgdbのthread apply all bt、またはgcoreでコアファイルを取っておき、再起動した後で分析 - 該当する場合:2つ以上のスレッドが、互いに相手の取ったロックを待つスタックのまま止まっており、その間プロセスのCPU使用率が0に近い - 該当しない場合:止まっている間にスレッド1つがCPUを100%使って回り続けていれば無限ループ。各スレッドがDB・外部の応答を待っていれば、同期呼び出しかスレッドプールの枯渇の側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Runtime locking correctness validator](https://docs.kernel.org/locking/lockdep-design.html) · Linux kernel · 2つのロックを互いに逆の順序で取ると循環待ちでデッドロック(lock inversion deadlock)、Linuxカーネルはロックの順序を検査して事前に警告 - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · 実行中なのに処理が進まないデッドロック状態をLiveness Probeで検知し、コンテナを再起動 - [Diagnostic Tools (Java SE 21 Troubleshooting Guide)](https://docs.oracle.com/en/java/javase/21/troubleshoot/diagnostic-tools.html) · Oracle · jstackは実行中のJVMの全スレッドのスタックを出力し、デッドロックも検出して表示(Found one Java-level deadlock) - [dotnet-stack diagnostic tool - .NET CLI](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-stack) · Microsoft · .NETプロセスの全スレッドのマネージドスタックをキャプチャして出力 - [Threads (Debugging with GDB)](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Threads.html) · GNU Project · thread apply allで全スレッドに同じコマンド(bt:コールスタックの出力)を実行 - [gcore(1) — Linux manual page](https://man7.org/linux/man-pages/man1/gcore.1.html) · gdb · 実行中のプログラムのコアファイルを作成し、作成後もプログラムはそのまま実行を続ける #### sp-sync-call · ゲームスレッドの同期呼び出し · Synchronous DB / file I/O on the game loop ティックの途中でDBの応答やファイルの書き込みを待つと、その時間だけサーバーのゲーム進行全体が止まります。 - なぜ → すると → 画面では:ティック内でDBの参照・保存、ログの書き込み、外部APIの呼び出しを待つ → DBに100msかかればティックも100ms止まる → DB・ディスクが遅くなるたびに、フィールド全体が一瞬止まる - 症状:フリーズ, カクつき / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:特定の操作をしたとき, ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:遅い処理(DBの参照・保存、ログの書き込み、外部APIの呼び出し)はすべて非同期に回し、結果は次のティックで反映(タイムアウトを設けるだけでは、待っている間はやはり止まる)。 - 数値の目安:同じデータセンター内のDBとの往復が0.5msでも、1ティックに100回呼べば50msです。20ティックのバジェットをそれだけで使い切ります。 - グラフでは:不定期なスパイク(サーバーのティック時間、DBクエリの遅延) - 確認箇所:ティック時間のグラフとDBクエリの遅延(slow query logなど)・ディスクの遅延を同じ時間軸に並べて確認。ティックのメトリクスがなければ、bccのoffcputime -pでゲームスレッドがどこで待っているか - 該当する場合:ティックが跳ねた時刻とDB・ファイルの遅延が跳ねた時刻が重なり、ゲームスレッドの待ち時間がDB応答の受信・ファイル書き込みのコールスタックに集中している - 該当しない場合:DB・ディスクの遅延が平常なのにティックが跳ねるなら、GC停止かロック競合の側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Designs, Lessons and Advice from Building Large Distributed Systems](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · LADIS 2009の基調講演(Jeff Dean)。同じデータセンター内の往復は約0.5ms(500,000ns) - [ASP.NET Core Best Practices](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/best-practices) · Microsoft · データアクセス・I/O・時間のかかる処理は非同期で呼び出す、同期のブロッキング呼び出しはスレッドプールの枯渇と応答の遅れを招く - [Demonstrations of offcputime, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/offcputime_example.txt) · IO Visor · スレッドが止まってCPUを離れていた時間(off-CPU)をコールスタックごとに集計、-pでプロセスを指定 #### sp-queue · メッセージキューの滞留 · Mailbox / job queue backlog リクエストが処理速度より速く届いてキューにたまると、後ろのリクエストは数秒後にやっと処理されるか、破棄されます。 - なぜ → すると → 画面では:リクエストが処理速度より速く到着 → キューが長くなり、上限を超えると破棄 → スキル・取引の反応が遅れるか、不発になる - 症状:入力遅延, 不発・ロールバック / 要因:遅延, パケットロス - 誰に:特定の場所・チャンネル, 特定の機能だけ / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:キュー長の監視、古いリクエストから破棄するポリシー、処理の並列化。 - グラフでは:上限で頭打ち(キュー長・最も古いメッセージの経過時間、毎秒の処理数) - 確認箇所:サーバーが記録するキューごとの長さ、最も古いメッセージの経過時間、毎秒の到着数・処理数・破棄数。コード側のメトリクスがなければ、ss(またはnetstat)でゲームソケットのRecv-Q(カーネルが受信済みで、プロセスがまだ読んでいない量) - 該当する場合:到着数が処理数を上回っている間、処理数はある値で頭打ちになり、キュー長・経過時間と破棄数が増え続ける - 該当しない場合:キューが短く経過時間も小さいのに反応が遅ければ、回線の遅延かティック自体の遅れの側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 実際の事例:eve-hedgp-2014 - 出典: - [Avoiding insurmountable queue backlogs](https://d1.awsstatic.com/builderslibrary/pdfs/avoiding-insurmountable-queue-backlogs.pdf) · AWS · Amazon Builders' Library。滞留は待機中のメッセージの経過時間で監視する、リアルタイムシステムは新しいデータから処理し(LIFOに近い形)、古いメッセージは捨てることもある - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · 処理速度よりリクエストの到着が速いとキューが埋まって遅延が増える、FIFOの代わりにLIFOやCoDelを使い、すでに意味のなくなった古いリクエストを間引く - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ソケットの統計を表示するツール(netstatと同様の情報)、-pでソケットを使っているプロセスを表示 - [netstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/netstat.8.html) · net-tools · Recv-Q:接続済みソケットで、ユーザープログラムがまだ読み取っていないバイト数 #### sp-timer-burst · タイマーの一斉発火 · Synchronized timers すべてのモンスターのリスポーン、すべてのバフの期限切れ、毎正時の報酬が同じティックに集中すると、そのティックだけ処理が数十倍重くなります。 - なぜ → すると → 画面では:リスポーン・期限切れ・報酬・オートセーブのタイマーが同じ時刻にそろっている → その1ティックに普段の数十倍の処理 → 決まった時刻になるたびに一瞬止まる - 症状:フリーズ, カクつき / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:一定の周期で - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:タイマーの時刻をランダムに少しずつ分散、複数のティックに分けて処理。 - グラフでは:周期的なスパイク(サーバーのティック時間) - 確認箇所:ティック時間が跳ねた時刻を集めて間隔を確認(毎正時、5分ごとなど)。同じ時刻に動くリスポーン・バフの期限切れ・報酬・オートセーブのタイマー一覧と照合 - 該当する場合:ティックが毎回同じ時刻か同じ間隔で跳ね、その時刻に一斉に発火するゲームのタイマー処理がある - 該当しない場合:周期はあるが、GCログの停止時刻やサーバーのcron・バックアップの時刻と重なるなら、サーバーGCの全停止か定期実行ジョブの側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Timeouts, retries, and backoff with jitter](https://d1.awsstatic.com/builderslibrary/pdfs/timeouts-retries-and-backoff-with-jitter.pdf) · AWS · Amazon Builders' Library。すべてのタイマー・周期処理・遅延実行の処理にジッターを加えて、同じ時刻に集中する負荷を散らす、複数サーバーの1分周期のリクエストが毎分最初の数秒に集中した事例 #### sp-pathfinding · 経路探索の急増 · Pathfinding storms 数百体のモンスターが同時にプレイヤーを追いかけて経路を計算すると、CPUを大きく消費します。 - なぜ → すると → 画面では:まとめ狩りや大量スポーンで、モンスターが一斉にプレイヤーを追跡 → モンスターごとに経路探索を計算 → その狩場だけスローモーション - 症状:スローモーション / 要因:ストール - 誰に:特定の場所・チャンネル / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:経路のキャッシュ、計算回数の上限、複数のティックへの分割。 - グラフでは:人数・負荷に連動して上昇(サーバーのティック時間、ゾーンごとのモンスター数) - 確認箇所:ゾーンごとの、プレイヤーを追跡中のモンスター数とティック時間。個別に数えた値がなければ、perf top -pでゲームプロセスの関数ごとのCPU比率 - 該当する場合:まとめ狩り・大量スポーンのときにティック時間が延び、経路探索(パスファインディング)の関数がCPU時間の大きな割合を占める - 該当しない場合:モンスターは少なくプレイヤーだけが多いときにティックが延びるなら、視界計算かブロードキャストの側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [AI.NavMesh.pathfindingIterationsPerFrame](https://docs.unity3d.com/ScriptReference/AI.NavMesh-pathfindingIterationsPerFrame.html) · Unity · 経路探索をフレームごとに決まったノード数だけ処理して複数フレームに分ける、経路が長いときやリクエストが一度に多いときもゲームが滑らかに動く - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · 実行中のプロセス(-p)のCPU使用比率を関数(シンボル)ごとにリアルタイム表示 #### sp-serialize · シリアライズ・圧縮のコスト · Serialization / compression cost 送るデータをバイト列に変換して圧縮するのにもCPUを使い、人が多いとこのコストが急増します。 - なぜ → すると → 画面では:更新のたびに構造体をバイト列に変換・圧縮 → 人数の2乗に比例してコストが増加 → 送信が遅れて入力遅延 - 症状:入力遅延 / 要因:ストール, 遅延 - 誰に:特定の場所・チャンネル / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:一度作ったパケットを複数人に再利用、軽量なフォーマット。 - グラフでは:人数・負荷に連動して上昇(サーバーのCPU使用率、パケットを作るスレッドのCPU) - 確認箇所:perf top -pで、ゲームプロセスのCPU時間に占めるシリアライズ・圧縮・暗号化の関数(zlib・LZ4・OpenSSLのようなライブラリの関数を含む)の比率を、人数が少ないときと集中したときで比較 - 該当する場合:人数が集中するほどシリアライズ・圧縮・暗号化の関数の比率が大きくなり、パケットを作るスレッドのCPUが先に飽和 - 該当しない場合:これらの関数の比率が小さければ、視界計算・ゲームロジックの側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:パケットを暗号化する接続(TLS・DTLSなど)なら、暗号化・復号にもCPUを使います。暗号化は接続ごとに個別に行うため、一度作ったパケットを複数人に再利用しても、暗号化のコストは受信者の数だけかかります。AES-GCMのような共通鍵暗号はコア1つで毎秒数GBを処理できるほど速く、普段の比率は小さいですが、一度に暗号化する単位(レコード)のサイズによって速度が大きく変わるため、ゲームのように小さいパケットが多いとバイトあたりのコストが大きくなります。接続ごとに一度行うハンドシェイクでは、サーバーが証明書の鍵で署名し、鍵交換(ECDHE)を計算します。コア1つあたり毎秒の署名は約1,100回(RSA 2048)から1万8千回(ECDSA P-256)、鍵交換は約9千回程度なので、ログインが集中すると負担になります。 - 出典: - [Introduction to Iris in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/introduction-to-iris-in-unreal-engine) · Epic Games · レプリケーションする状態を量子化したコピー1つで保持して重い処理を減らし、その処理を複数の接続で共有 - [VALORANT's 128-Tick Servers](https://www.riotgames.com/en/news/valorants-128-tick-servers) · Riot Games · 毎フレーム、レプリケーション変数をクライアントごとに比較して変わった値をまとめる方式は、メモリをあちこち読む遅い処理なので、サーバーのCPUを大きく消費 - [How "expensive" is crypto anyway?](https://blog.cloudflare.com/how-expensive-is-crypto-anyway/) · Cloudflare · BoringSSLでの測定:AES-128-GCMは毎秒約3.7GB(レコードサイズによって大きく変わる)、コア1つあたり毎秒RSA 2048署名1,120回・ECDSA P-256署名18,477回・P-256 ECDHE 9,394回、CloudflareのエッジサーバーでTLSライブラリが使ったCPUは約1.8% - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · 実行中のプロセス(-p)のCPU使用比率を関数(シンボル)ごとにリアルタイム表示 #### sp-crash · サーバークラッシュ · Server process crash 未処理のエラーでサーバープロセスが落ちると、そのサーバーにいた全員が同時に切断されます。 - なぜ → すると → 画面では:存在しない対象を参照するエラー(null参照)、不正なデータ、メモリ不足などの致命的なエラー → サーバー(またはゾーン)のプロセスが終了 → 全員が同時に切断、最後の保存以降の進行はロールバックされることがある - 症状:切断, 不発・ロールバック / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:ときどきランダムに, 特定の操作をしたとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:クラッシュダンプの分析による原因の修正、こまめな保存。 - インフラチームの対応:プロセスの自動再起動、クラッシュダンプの収集・保管環境、サーバーダウン時の即時アラート。 - グラフでは:接続が一斉に切れる(接続数、プロセスの再起動回数) - 確認箇所:coredumpctl listのコアダンプ記録(時刻、PID、終了シグナル)と、サービスマネージャー(systemd)の異常終了・再起動の記録。WindowsサーバーはWERが残したダンプファイル - 該当する場合:接続数が一瞬で0近くまで落ちた時刻に、ゲームサーバープロセスの異常終了とコアダンプがある - 該当しない場合:プロセスは動き続けているのに接続が切れたなら、ネットワーク機器・アイドルタイムアウトの側。長く止まった後にウォッチドッグが再起動した記録なら、無限ループ・デッドロックの側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Collecting User-Mode Dumps](https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps) · Microsoft · Windowsエラー報告(WER)で、ユーザーモードのプログラムがクラッシュしたときにフルダンプ・ミニダンプをローカルに収集するよう設定 - [systemd.service(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd.service.5.html) · systemd · Restart=on-failureは、異常終了・シグナルによる終了(コアダンプを含む)・ウォッチドッグのタイムアウト時にサービスを自動再起動、長時間動くサービスに推奨 - [coredumpctl(1) — Linux manual page](https://man7.org/linux/man-pages/man1/coredumpctl.1.html) · systemd · systemd-coredumpが保存したコアダンプをlistで一覧表示、クラッシュの時刻・PID・クラッシュを起こしたシグナルを表示 #### sp-threadpool · スレッドプールの枯渇 · Thread pool starvation 処理を担うワーカースレッドがすべて遅い処理に塞がれると、新しいリクエストはいつまでも待たされます。 - なぜ → すると → 画面では:ワーカースレッドが外部API・DBの応答待ちで塞がる → 新しいリクエストに割り当てるスレッドがない → ログイン・ショップなど特定の機能が無限ロード - 症状:接続不可・無限ロード, 入力遅延, フリーズ / 要因:ストール - 誰に:特定の機能だけ, サーバー全体 / いつ:人が集中したとき, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:遅い呼び出しにタイムアウト、機能ごとのスレッドプール分離、非同期化。 - グラフでは:上限で頭打ち(スレッドプールのスレッド数・キュー長、リクエストの処理時間) - 確認箇所:.NETはdotnet-counters monitorでスレッドプールのスレッド数とキュー長(.NET 9以降はdotnet.thread_pool.thread.count・dotnet.thread_pool.queue.length、8以前はThreadPool Thread Count・ThreadPool Queue Length)を確認し、dotnet-stackでワーカースレッドがどこで待っているかを確認。JVM・ネイティブのサーバーはスレッドダンプで同じことを確認 - 該当する場合:CPU使用率は100%よりかなり低いのに、スレッド数がゆっくり増え続けるか上限に張り付いており、キューがたまり、ワーカーの大半が同じ外部呼び出し(DB・HTTP)の応答を待っている - 該当しない場合:キューが空なのに遅ければ呼び出し先そのものが遅いので、カスケード障害・外部サービスへの依存の側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:パケットの受信とゲームロジックが同じワーカースレッドプールを共有する構造では、数個の遅い処理がワーカーをすべて占有した瞬間に、サーバー全体のパケット処理が止まります。 - 実際の事例:riot-euw-2021 - 出典: - [Debug ThreadPool Starvation](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/debug-threadpool-starvation) · Microsoft · プールに空きスレッドがなく新しい処理が待たされると応答が遅くなる、スレッドを占有するブロッキングコードが原因。dotnet-countersでCPUは100%よりかなり低いのにdotnet.thread_pool.thread.countがゆっくり増え続けるなら枯渇のサイン(dotnet.thread_pool.queue.lengthも大きいことが多い)、dotnet-stackでスレッドが待っている箇所を確認 - [Avoiding insurmountable queue backlogs](https://d1.awsstatic.com/builderslibrary/pdfs/avoiding-insurmountable-queue-backlogs.pdf) · AWS · 同時処理数 = 到着率 × 遅延(リトルの法則)。毎秒100件で遅延が100msから10秒に延びると、スレッドが10本から1,000本に増えてプールが枯渇 - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · Microsoft Azure · 呼び出し先ごとにコネクションプール・スレッドプールを分けておけば、1つの呼び出し先の障害はそのプールだけを塞ぐ - [.NET runtime metrics](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-runtime) · .NET · dotnet.thread_pool.thread.count(スレッドプールのスレッド数)・dotnet.thread_pool.queue.length(待機中の処理数)は.NET 9から - [Well-known EventCounters in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/available-counters) · Microsoft · .NET 8以前のThreadPool Thread Count(threadpool-thread-count)・ThreadPool Queue Length(threadpool-queue-length) #### sp-infinite-loop · 無限ループ・ロジックの暴走 · Infinite loop / runaway logic バグで1ティックが終わらないとサーバーが止まり、ウォッチドッグが強制的に再起動します。 - なぜ → すると → 画面では:誤った条件でループが終わらない、または再帰が暴走 → ティックが終わらずサーバーが停止 → フリーズの後、全員が切断 - 症状:フリーズ, 切断 / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:特定の操作をしたとき, ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ループ回数の上限、ウォッチドッグ、問題の入力を再現するテスト。 - グラフでは:接続が一斉に切れる(接続数、スレッドごとのCPU) - 確認箇所:止まっている間にpidstat -t 1でスレッドごとのCPUを確認し、100%で回っているスレッドがどの関数で回っているかをperf top -t(スレッドID)かgdbで確認。すでに再起動していれば、ウォッチドッグのタイムアウト記録(systemdのWatchdogSec、KubernetesのLiveness Probe失敗) - 該当する場合:サーバーが止まっている間、ゲームスレッド1つがCPU使用率100%に張り付いており、スタックが同じ関数・ループの中で回り続けている - 該当しない場合:止まっている間CPUが0に近ければ、デッドロックか外部の応答待ちの側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [systemd.service(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd.service.5.html) · systemd · WatchdogSec=:サービスが決められた時間内に生存通知(WATCHDOG=1)を送らなければ失敗とみなして終了、Restart=の設定に従って自動再起動 - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · 実行中なのに処理が進まない状態をLiveness Probeで検知して再起動、デフォルトでは10秒ごとに検査し、3回連続で失敗すると再起動 - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -tで、プロセスに属するスレッドごとの統計(CPU使用率など)も表示 - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · 実行中のスレッド(-t)やプロセス(-p)のCPU使用比率を関数(シンボル)ごとにリアルタイム表示 #### sp-hot-entity · 1体に集中する戦闘(ワールドボス) · Hot entity / combat event fan-out 数百人が1体のボスを同時に攻撃すると、そのボス1体の計算が1か所に集中し、ヒット情報が見ている全員に送信されます。 - なぜ → すると → 画面では:数百人が1体のボスにスキル・バフ・デバフを絶え間なく使用 → ボスのHP・ヘイトリスト・デバフの計算が1か所に集中し、ヒットのたびにダメージ数値・エフェクトのパケットを見ている全員に送信 → スキルの反映が遅れ、ダメージ数値がまとめて表示される、ボスの周辺だけスローモーション - 症状:入力遅延, 早送り, スローモーション / 要因:ストール, 遅延 - 誰に:特定の場所・チャンネル / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ほかのプレイヤーのダメージ数値・エフェクトはまとめるか省略、1つの対象にかかるデバフ数の上限、ヒット処理を複数のティックに分割。 - 数値の目安:800人が毎秒2回ずつ攻撃すれば、毎秒1,600回のヒットです。これを見ている800人全員に通知すると、毎秒128万個のメッセージになります。 - グラフでは:人数・負荷に連動して上昇(サーバーのティック時間、送信メッセージ数) - 確認箇所:ボス戦の時間帯のティック時間と送信パケット数を、ボス周辺の人数と並べて確認し、可能なら対象ごとの毎秒のイベント(ヒット・バフ・デバフ)数 - 該当する場合:ボス周辺の人数が増えるとティック時間と送信量が急激に増え、ボス1体の毎秒のイベント数がほかの対象より数十倍多い - 該当しない場合:ボスと関係なく1か所に集まるだけで同じように遅くなるなら、視界計算かブロードキャストの急増の側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · 1回の攻撃でも見ているすべてのクライアントに通知する必要があるため、n人がn人に通知するO(n²)の負荷が生じ、メッセージの多いドローン攻撃ではこの負荷がさらに速く大きくなる #### sp-spawn-burst · 密集エリア進入時のスポーン集中 · Spawn burst when entering a crowd 人でいっぱいの街にテレポートすると、サーバーは新たに見えるようになった数百人の外見・装備・状態を一度に送らなければなりません。 - なぜ → すると → 画面では:テレポート・ログイン・チャンネル移動で、混雑した場所に突然現れる → 数百人分の全情報を一度に作って送り、自分のPCも一度に読み込む → 到着直後に一瞬止まる、キャラクターが遅れて1体ずつ現れ、入力の反応が遅い - 症状:フリーズ, 入力遅延, 早送り / 要因:ストール, 遅延 - 誰に:自分だけ, 特定の場所・チャンネル / いつ:移動中・マップ切り替え時, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:近い順に複数のティックに分けて送信、外見情報のキャッシュ。クライアント:ロード画面の間に先に受信、受け取ったキャラクターを複数フレームに分けて生成。 - 数値の目安:1人分の外見・装備・バフの情報が300バイトなら、500人で約150KBです。普段の1ティックの送信量(数KB)の数十倍が一瞬に集中します。 - グラフでは:接続直後・メンテ明けに急増(接続ごとの送信バイト数、クライアントのフレームタイム) - 確認箇所:混雑した場所に到着した直後の数秒間に、その接続へ送ったバイト数・パケット数(サーバーログ)と、クライアントのフレームタイム(ネットグラフ・クライアントログ) - 該当する場合:到着直後にその接続の送信量が普段の1ティックの数十倍に跳ね上がってから落ち着き、同じ瞬間にクライアントのフレームタイムも跳ねる - 該当しない場合:人の少ない場所へ移動しても同じように止まるなら、ゾーン移動(サーバー間の引き継ぎ)かクライアントのロードの側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Detailed Actor Replication Flow in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/detailed-actor-replication-flow-in-unreal-engine) · Epic Games · アクターチャネルを最初に開くときに初期位置・回転などの情報も一緒に送り、接続が飽和したら残りのアクターは次のティックに回す - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · 距離と視線の向きで優先度を付け、近くて見えるアクターから送る #### sp-entity-buildup · オブジェクトの蓄積(消えずに残るアイテム・召喚物) · Entity / timer buildup over uptime 消えるはずの地面のアイテム、召喚物、終わったタイマーが片付けられずにたまると、サーバーを長く稼働させるほど毎ティックの処理が増えます。 - なぜ → すると → 画面では:地面のアイテム・召喚物・期限切れのタイマー・空のパーティ情報が、本来のタイミングで削除されない → ティックごとに走査するリストが日ごとに長くなる → メンテ明けは問題ないが、数日たつとそのサーバー・エリアだけ次第に重くなる - 症状:スローモーション, カクつき, 入力遅延 / 要因:ストール - 誰に:サーバー全体, 特定の場所・チャンネル / いつ:長時間稼働するほど - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:エリアごとのオブジェクト数をメトリクスとして記録して傾向を確認、オブジェクトごとの寿命と個数の上限、定期的なクリーンアップ。 - 数値の目安:ティックごとにすべてのオブジェクトを1回ずつ走査するサーバーなら、オブジェクト数が2倍になると、その部分のティック時間も2倍になります。 - グラフでは:徐々に上昇して急落(ゾーンごとのオブジェクト数、サーバーのティック時間) - 確認箇所:ゾーン・サーバーごとのオブジェクト数(地面のアイテム・召喚物・タイマー)とティック時間を、メンテナンスの周期より長い期間(数週間)で確認 - 該当する場合:メンテ明けに低い値から始まったオブジェクト数とティック時間が日ごとに上がり、メンテナンス・再起動のたびにがくんと下がることを繰り返し、その間メモリには余裕がある - 該当しない場合:ティックは変わらないのにメモリだけが上がり続けるなら、メモリリークの側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:メモリリークと同じく長く稼働するほど悪化しますが、メモリには余裕があるのにティック時間だけが延びる点が異なります。オブジェクト数のグラフがメンテナンスの周期ごとにのこぎり状になっていれば、このケースです。 - 出典: - [Actor Ticking in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-ticking-in-unreal-engine) · Epic Games · アクター・コンポーネントは間隔を個別に設定しなければ毎フレーム1回ティックが実行され、不要ならティックを無効にできる - [AActor::SetLifeSpan](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/AActor/SetLifeSpan) · Epic Games · アクターに寿命を設定すると、期限が来たときに自動で破棄 #### sp-patch-traffic · アップデートによるトラフィックパターンの変化 · Patch changes traffic pattern 新しいコンテンツ・エフェクト・同期項目がパケットのサイズと頻度を増やすと、問題なく動いていたサーバーがアップデート後からMTU・帯域幅・パケット数の上限に引っかかります。 - なぜ → すると → 画面では:アップデートで新しいスキルエフェクト・同期項目・アイテム情報が増え、パケットが大きくなるか頻繁になる → 大きなパケットはMTUを超えてフラグメント化され、増えた分は帯域幅・クラウドのPPS上限・送信バッファに引っかかる → アップデート直後から、混雑した場所でワープ・スキル不発・入力遅延。インフラは何も変えていないのにパケットロスが増える - 症状:ワープ, 不発・ロールバック, 入力遅延 / 要因:パケットロス, 遅延 - 誰に:サーバー全体, 特定の場所・チャンネル, 特定の地域・ISP / いつ:人が集中したとき, 夜のピーク時間帯, 常に - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ, インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:パケットを1,200バイト以下に自前で分割、新しい同期項目は変更分だけを送り、距離・重要度に応じて頻度を下げる、デプロイ前にテストサーバーでユーザーあたりの毎秒パケット数・バイト数と最大パケットサイズを前のビルドと比較、トラフィックのメトリクスにビルドバージョンを記録。 - インフラチームの対応:サーバー機器・OS:デプロイ時刻をグラフに表示し、ユーザーあたりの毎秒パケット数・バイト数と平均パケットサイズをデプロイ前後で比較、インスタンスの上限超過カウンターにアラート、必要ならより大きなインスタンス。ネットワーク:ファイアウォール・ロードバランサー・DDoS対策機器の処理上限と、フラグメントを遮断していないかを点検。 - 数値の目安:UDPパケットは1,200バイト以下なら安全で、インターネットの経路MTUは通常1,500バイト、トンネルを通るとさらに小さくなります(GREトンネルなら1,476バイト)。経路MTUを超えたパケットはフラグメント化されるか破棄され、フラグメント化されたパケットはフラグメントを1つ失うだけで全体が失われます。ユーザーあたりの毎秒パケット数が20%増えればサーバー全体でも20%増えるため、上限近くで使っていたインスタンスはすぐにあふれます。 - グラフでは:ある時点から階段状に上昇(ユーザーあたりの毎秒パケット数・バイト数、平均パケットサイズ) - 確認箇所:デプロイ時刻の前後で、サーバーのNICの毎秒パケット数・バイト数(sar -n DEVのrxpck/s・txpck/s・rxkB/s・txkB/s、EC2ではNetworkPacketsOut・NetworkOut)を同時接続数で割って比較。平均パケットサイズはバイト数 ÷ パケット数、サイズの分布はパケットキャプチャをWiresharkのPacket Lengths統計で確認 - 該当する場合:デプロイ直後からユーザーあたりのパケット数・バイト数や平均パケットサイズが一段上がったまま推移し、同じ時刻からサーバーが作ったフラグメント数(sar -n IPのfragcrt/s)やインスタンスの上限超過カウンター(AWS ENAのpps_allowance_exceeded・bw_out_allowance_exceeded)が増える - 該当しない場合:トラフィックパターンはデプロイ前後で同じなのに遅延・パケットロスだけが増えたなら、同じ時刻のインフラ変更(設定、経路、機器、OS・カーネルのアップデート)を確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:「アップデート前は問題なかった」という報告が来たら、インフラの変更と合わせて最初に確認すべきゲーム側の原因です。パッチノートにネットワークの変更がなくても、新しいエフェクトや同期項目が1つ増えるだけで、混雑した場所では数百人分に掛け算されます。増えたトラフィックが実際に引っかかる箇所は、「UDPパケットのIPフラグメンテーション」「クラウドのPPS上限超過」「NIC帯域幅の飽和」「カーネルのソケットバッファ不足」「中間機器の処理上限超過(ファイアウォール・IPS・DDoS対策)」の各項目で扱います。この項目は、その上限に達する出発点がゲームのアップデートだった場合なので、上限を引き上げる前に、アップデートで増えたトラフィックをまず減らします。同じ時刻にOS・カーネルもアップデートしていたなら、ユーザーあたりのトラフィックが変わったかどうかで「OS・カーネル・ドライバー・ファームウェアのアップデート後の性能変化」と切り分けます。 - 出典: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · UDPアプリケーションは経路MTUを超えるデータグラムを送るべきではない(SHOULD NOT)、フラグメントを1つ失うとフラグメント化されたパケット全体が失われ、一部のNAT・ファイアウォールはフラグメントをすべて破棄 - [RFC 8899: Packetization Layer Path MTU Discovery for Datagram Transports](https://www.rfc-editor.org/rfc/rfc8899) · IETF · UDPなどのデータグラム通信の基本となる安全なサイズ(BASE_PLPMTU)として1,200バイトを推奨 - [Maximum transmission unit and maximum segment size](https://developers.cloudflare.com/magic-transit/reference/mtu-mss/) · Cloudflare · インターネットの経路MTUは1,500、GREトンネルを通ると1,476 - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · pps_allowance_exceeded・bw_out_allowance_exceeded:インスタンスのPPS・送信帯域幅の上限を超えて、キューにたまったか破棄されたパケットの数 - [sar(1) — Linux manual page](https://man7.org/linux/man-pages/man1/sar.1.html) · sysstat · sar -n DEVのrxpck/s・txpck/s(毎秒のパケット数)・rxkB/s・txkB/s(毎秒のKB)、sar -n IPのfragcrt/s(毎秒作成したIPフラグメント数、ipFragCreates) - [CloudWatch metrics that are available for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · NetworkPacketsOut(インスタンスがすべてのネットワークインターフェースから送信したパケット数)・NetworkOut(送信したバイト数) - [8.7. Packet Lengths](https://www.wireshark.org/docs/wsug_html_chunked/ChStatPacketLengths.html) · Wireshark · キャプチャしたパケットを長さの区間ごとに分け、件数・平均・最小・最大を表示 ### L10 メモリ(原因9件) #### mem-gc · サーバーGCの全停止 · Stop-the-world GC pause Java・C#のサーバーがガベージを回収するために全スレッドを止めている間(stop-the-world)、サーバー全体が止まります。 - なぜ → すると → 画面では:ヒープが埋まってGCが始まる → 全ゲームスレッドを止めて回収(生存データが多いほど長引く) → サーバー上の全員が同時にフリーズし、その後早送り - 症状:フリーズ, 早送り / 要因:ストール - 誰に:サーバー全体 / いつ:一定の周期で, 長時間稼働するほど - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:停止時間の短いGC(ZGC、Shenandoah、目標時間を短くしたG1)を起動オプションで明示的に指定、アロケーションの削減、ヒープサイズの調整。 - インフラチームの対応:ヒープを十分に確保できるメモリ量のインスタンス、コンテナにはCPUを2個以上・メモリを約1.8GB以上割り当て(JDK 26以前はそれより小さいとSerial GCがデフォルトで選ばれる)、GC停止時間の監視。 - 数値の目安:新しいオブジェクト(Young領域)だけを回収するMinor GCは数〜数十ms。生存データが数GBあるヒープ全体を回収するFull GCは1秒を超えることもあります。ZGCはヒープサイズにほとんど関係なく1ms未満で、Shenandoahも停止時間がヒープサイズに比例しないため短く済みます。 - グラフでは:周期的なスパイク(サーバーのティック時間、GC停止時間) - 確認箇所:GCログを有効にし、停止した時刻と長さをサーバーのティック時間グラフに重ねて確認。Javaは起動オプション-Xlog:gc*(JDK 8以前は-XX:+PrintGCDetails)のPause行、.NETはdotnet-countersのGC停止メトリクス(.NET 9以降はdotnet.gc.pause.time、8以前は% Time in GC since last GC)、GoはGODEBUG=gctrace=1がGCごとに出力する行を確認 - 該当する場合:ティックが跳ねた時刻とGC停止の時刻が重なり、停止の長さが跳ねた長さとほぼ同じ。サーバーのすべてのゾーン・チャンネルが同じ瞬間に跳ねる - 該当しない場合:GCログに長い停止がないのにティックが跳ねるなら、ロック・同期呼び出し・ディスク書き込みなど別の原因。1つのゾーンだけ跳ねるなら、スクリプトエンジンのGC(mem-script-gc)かそのゾーンの負荷 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:JavaのG1は1回あたりの停止目標がデフォルトで200msなので、20ティックのサーバーでは4ティック分に相当します。コンテナにCPUを2個未満、またはメモリを約1.8GB未満しか割り当てないと、JDK 26以前のJavaは単一スレッドで回収するSerial GCをデフォルトのGCとして選び、停止がはるかに長くなります。C#(.NET)のサーバーは通常サーバーGCとバックグラウンドGCを有効にしますが、新しいオブジェクトを入れる第0・第1世代(Gen0・1)の回収と、コンパクション(圧縮)を伴うFull GCは依然として全スレッドを止めます。Goは停止が通常1ms未満ですが、アロケーションが多いとメモリを要求した側がGC作業を分担しなければならず、ティックが遅くなります。どの方式でも、アロケーションが回収より速ければ最終的にゲームスレッドが止まります。G1はFull GCに移行し、ZGCはメモリを要求したスレッドを回収が終わるまで止めます。 - 実際の事例:riot-euw-2021 - 出典: - [Garbage-First (G1) Garbage Collector](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-first-g1-garbage-collector1.html) · Oracle · G1の停止目標のデフォルト値は200ms(MaxGCPauseMillis)、回収中にメモリが尽きるとヒープ全体を止めてコンパクションするFull GCに移行 - [JEP 523: Make G1 the Default Garbage Collector in All Environments](https://openjdk.org/jeps/523) · OpenJDK · これまではCPUが1個、またはメモリが1792MB未満だとSerial GCがデフォルトで選ばれ、JDK 27からはどの環境でもG1がデフォルト - [JEP 439: Generational ZGC](https://openjdk.org/jeps/439) · OpenJDK · ZGCの停止は1ms以下でヒープサイズに依存しない、G1の停止は数ms〜数秒。アロケーションが回収より速いとアロケーションストール(allocation stall)のおそれ - [JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)](https://openjdk.org/jeps/189) · OpenJDK · Shenandoahの停止時間はヒープが200MBでも200GBでもほぼ同じ - [Background garbage collection](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/background-gc) · .NET · バックグラウンドGCは第2世代の回収にのみ適用され、第0・第1世代の回収(フォアグラウンドGC)はマネージドスレッドをすべて止める - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · Go GCは大部分が並行して動き、短い全停止があるだけ。アロケーションが多いとゴルーチンがGC作業を肩代わりし(assist)、遅延が発生 - [JEP 271: Unified GC Logging](https://openjdk.org/jeps/271) · OpenJDK · JDK 9からGCログを統合ロギング(-Xlog)で再実装、-Xlog:gcは以前の-XX:+PrintGCと同様にGCごとに1行 - [The java Command](https://docs.oracle.com/en/java/javase/25/docs/specs/man/java.html) · Oracle · 旧GCログオプションを-Xlogに置き換える対応表:-XX:+PrintGCDetailsは-Xlog:gc* - [dotnet-counters diagnostic tool](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) · .NET · .NET 9以降はSystem.Runtimeのメーター(dotnet.gc.pause.timeなど)、.NET 8以前は旧EventCounter(% Time in GC since last GCなど)で表示 - [runtime package](https://pkg.go.dev/runtime) · Go · GODEBUG=gctrace=1:GCごとに1行、フェーズごとの経過時間(ウォールクロック)、GC開始時・終了時のヒープサイズと目標ヒープ #### mem-script-gc · スクリプトエンジンのGC停止 · Scripting VM GC (Lua, etc.) C++のサーバーでも、クエスト・AI・スキルをLuaなどのスクリプトで動かしていると、スクリプトエンジンのGCが走っている間そのゾーンが止まります。 - なぜ → すると → 画面では:ゾーンごとにスクリプトエンジンがクエスト・AI・イベントを実行し、一時オブジェクトを大量に生成 → スクリプトエンジンのGCが一度に大量に回収すると、そのゾーンのティックが止まる → 特定のゾーン・特定のイベント中だけ周期的に一瞬止まる - 症状:カクつき, フリーズ / 要因:ストール - 誰に:特定の場所・チャンネル / いつ:人が集中したとき, 一定の周期で - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:インクリメンタル・世代別GCの設定、ティックごとに少しずつGCを進める、スクリプトの一時オブジェクトの削減。 - 数値の目安:スクリプトのヒープが数百MBまで膨らむと、一度にまとめて行う回収(インクリメンタル回収を無効にした場合や、世代別モードでの全体回収)に数十〜数百msかかることもあります。 - グラフでは:周期的なスパイク(ゾーン別のティック時間、スクリプトエンジンのメモリ) - 確認箇所:ゾーン別のティック時間と、そのゾーンのスクリプトエンジンのメモリ使用量(Luaはcollectgarbage("count"))をティックごとに記録し、1つのグラフに重ねて確認 - 該当する場合:スクリプトのメモリがガクッと下がる瞬間(一度にまとめて回収)とそのゾーンのティックのスパイクが重なり、他のゾーンは正常 - 該当しない場合:スクリプトのメモリに変化がないまま跳ねるなら、そのゾーンの負荷かロック。サーバーのすべてのゾーンが同時に跳ねるなら、サーバーのGC(mem-gc)かスワップ(mem-swap) - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Lua 5.4 Reference Manual](https://www.lua.org/manual/5.4/manual.html) · Lua.org · インクリメンタルモードは回収を小さなステップに分けて実行の合間に挟み込み(ステップを大きく取ると全停止)、世代別モードのmajor回収はすべてのオブジェクトをたどる全停止、collectgarbage("count")はLuaが使っているメモリの総量(KB) #### mem-alloc · アロケーションの急増 · Allocation storms イベント中に一時オブジェクトを大量に生成すると、GCが普段よりはるかに頻繁に走ります。 - なぜ → すると → 画面では:アイテムドロップ・戦闘ログ・イベント報酬で一時オブジェクトが急増 → GCが数倍の頻度で走り、まだ破棄されていなかったオブジェクトがOld領域に移ってFull GCも早まる → イベント時だけ周期的に一瞬止まる - 症状:カクつき, フリーズ / 要因:ストール - 誰に:サーバー全体, 特定の場所・チャンネル / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:オブジェクトプール、バッファの再利用、アロケーションのプロファイリング。 - グラフでは:人数・負荷に連動して上昇(GC回数、アロケーション速度) - 確認箇所:GCログ(Java -Xlog:gc、Go GODEBUG=gctrace=1)で1分あたりのGC回数を数え、.NETはdotnet-countersのアロケーション量とGC回数(.NET 9以降はdotnet.gc.heap.total_allocated・dotnet.gc.collections、8以前はAllocation Rate・Gen 0 GC Count)を確認。同時接続数・イベントの時刻と重ねて確認 - 該当する場合:イベントが始まると、アロケーション速度とGC回数が人数の増加より急激に増え、短い停止が頻発。イベントが終わると元に戻る - 該当しない場合:GC回数は変わらないのに1回の停止が長くなるなら、生存データが増えている(mem-gc-thrash、mem-leak) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · Young領域が埋まるとminor GC、生き残ったオブジェクトの一部がOld領域に移され、Oldが埋まるとヒープ全体を回収(minorよりはるかに時間がかかる)、-Xlog:gcはGCごとに1行ずつ記録 - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · アロケーション速度が高いほどGCサイクルが頻繁になる、GODEBUG=gctrace=1でGCトレースを出力 - [dotnet-counters diagnostic tool](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) · .NET · .NET 9以降はdotnet.gc.heap.total_allocated・dotnet.gc.collections、.NET 8以前はAllocation Rate・Gen 0 GC Countで表示 #### mem-leak · メモリリーク · Memory leak 解放されないメモリが少しずつたまり、数日後にGCの多発・スワップ・強制終了につながります。 - なぜ → すると → 画面では:ログアウトしたキャラクターの情報・イベントハンドラーが解放されない → 数日かけて空きメモリが減っていく → メンテ明けは正常、日を追うごとにラグが増え、最後はサーバーダウン - 症状:スローモーション, フリーズ, 切断 / 要因:ストール - 誰に:サーバー全体 / いつ:長時間稼働するほど, 夜のピーク時間帯 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:ヒープダンプの解析、長時間の負荷テスト。 - インフラチームの対応:プロセスごとのメモリ使用量の推移の監視とアラート。 - グラフでは:徐々に上昇(プロセスのメモリ(RSS)、GC直後のヒープ) - 確認箇所:ゲームサーバープロセスのメモリ(pidstat -rのRSS)を数日単位で確認し、GCを使うサーバーはGC直後に残ったヒープを確認。Javaは-Xlog:gcの行に出るGC前・後の使用量のうちGC後の値、.NETはdotnet-countersのGC後のヒープサイズ(.NET 9以降はdotnet.gc.last_collection.heap.size、8以前はGC Heap Size) - 該当する場合:GC直後に残るヒープ(ベースライン)が再起動後に日ごとに上がり、人数の少ない早朝にも下がらない - 該当しない場合:ヒープのベースラインは横ばいなのにRSSだけ上がるなら、断片化(mem-fragment)かネイティブメモリ側。人数に連動して上下するなら正常な使用量 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:定期メンテで毎週再起動していると、リークが隠れて長い間見つかりません。メンテが一度延期されたときや、イベントで人数が増えたときに突然表面化することがよくあります。 - 出典: - [Troubleshoot Memory Leaks](https://docs.oracle.com/en/java/javase/25/troubleshoot/troubleshooting-memory-leaks.html) · Oracle · 実行が徐々に遅くなるならリークを疑う、最終的にメモリが尽きて異常終了。リーク解析の中心となる資料はヒープダンプ - [Debug a memory leak in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/debug-memory-leak) · .NET · GCがあっても不要なオブジェクトを参照し続けるとリーク、性能低下とOutOfMemoryException。メモリの推移の確認とダンプの解析 - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · -Xlog:gcの行は「GC前の使用量->GC後の使用量(ヒープサイズ)」の形式 - [dotnet-counters diagnostic tool](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) · .NET · .NET 9以降はdotnet.gc.last_collection.heap.size、.NET 8以前はGC Heap Sizeで表示 - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -r:プロセスごとのRSS(実際にRAM上にあるメモリ)とページフォルト #### mem-gc-thrash · GCスラッシング(ヒープの空き不足) · GC thrashing (heap nearly full) 生存データがヒープの上限に近づくと、GCが走っても回収できるものがほとんどなく、GCが休みなく繰り返されます。 - なぜ → すると → 画面では:イベントでの人数増加やリークで、生存データがヒープの上限近くまで埋まる → GCがわずかしか回収できず、すぐにまたFull GC、CPUの大半をGCが使う → サーバー全体が数分間スローモーション・フリーズを繰り返し、メモリ不足で終了 - 症状:スローモーション, フリーズ, 切断 / 要因:ストール - 誰に:サーバー全体 / いつ:夜のピーク時間帯, 人が集中したとき, 長時間稼働するほど - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:ヒープをピーク時の生存データより余裕をもって(通常2倍以上)設定、長く参照し続けるデータ・リークの削減。 - インフラチームの対応:GC時間比率のアラート、粘らずに早めに再起動、ヒープを増やせるようメモリに余裕のあるインスタンス。 - 数値の目安:実行時間の10%を超えてGCに使っていれば、一般に危険信号とみなします。Javaの一部のGCは、時間の98%をGCに費やしてもほとんど回収できないとメモリ不足のエラーを出します。 - グラフでは:上限で頭打ち(GC直後のヒープ、GC時間比率) - 確認箇所:GC直後に残ったヒープが最大ヒープにどれだけ近いかと、GCに使った時間の比率を確認。Javaは-Xlog:gcの行の「GC後(ヒープサイズ)」とPause Full行の頻度、.NETはdotnet-counters(.NET 8以前は% Time in GC since last GC、.NET 9以降はdotnet.gc.pause.timeの増加量)、GoはGODEBUG=gctrace=1の行と行の間隔を確認 - 該当する場合:GC直後もヒープが最大値近くまで残り、Full GCが立て続けに走り、GC時間比率が普段より大きく(通常10%超)上がる。その間サーバー全体のティックがそろって遅くなる - 該当しない場合:GC直後のヒープに余裕があるのに停止だけが長いなら、GC方式・設定側(mem-gc) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [The Parallel Collector](https://docs.oracle.com/en/java/javase/25/gctuning/parallel-collector1.html) · Oracle · パラレルGCは全体時間の98%超をGCに費やしてもヒープの回収が2%未満だとOutOfMemoryError - [Garbage-First Garbage Collector Tuning](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-first-garbage-collector-tuning.html) · Oracle · G1はデフォルト(GCTimeRatio=12)でGC時間を全体の約8%以下に抑えるようヒープサイズを決める、ヒープ占有率が高すぎて起きたFull GCはログのPause Full (G1 Compaction Pause)で見つける - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · デフォルトのGOGC=100では目標ヒープが生存ヒープの約2倍、メモリ上限に張り付くとGCが休みなく走るスラッシング、GODEBUG=gctrace=1でGCトレースを出力 - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · -Xlog:gcの行はGCの種類(Pause Young、Pause Full)と「GC前の使用量->GC後の使用量(ヒープサイズ)」、停止時間 - [dotnet-counters diagnostic tool](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) · .NET · .NET 9以降はdotnet.gc.pause.time、.NET 8以前は% Time in GC since last GCで表示 #### mem-swap · スワップ · Swapping メモリが足りずOSが一部をディスクに退避させると、そのメモリを使うたびに1,000倍以上遅いディスクを待つことになります。 - なぜ → すると → 画面では:使用メモリが物理RAMを超える → OSが一部をディスクに退避させ、必要なときに読み戻す → ティックが数百msに跳ね上がり、サーバーの全ユーザーがスローモーション・フリーズ - 症状:スローモーション, フリーズ / 要因:ストール - 誰に:サーバー全体 / いつ:長時間稼働するほど, 夜のピーク時間帯 - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:プロセスのメモリ使用量に上限(ヒープサイズなど)を設け、リークを点検。 - インフラチームの対応:ゲームサーバーはスワップを使わない設定、メモリのアラートで対応、スワップを無効にするとメモリ不足の瞬間に強制終了(OOM)するため、ピーク使用量より余裕のあるRAMを確保。 - 数値の目安:RAMの読み出し約100ns、SSDからの読み戻し約100µs(1,000倍)、ネットワーク越しのクラウドディスクは約1ms(1万倍)、HDDは10ms(10万倍)。 - グラフでは:徐々に上昇(スワップ使用量、スワップイン・アウト) - 確認箇所:vmstat 1のsi・so列(1秒あたりにスワップから読み込んだ量・スワップへ書き出した量)、/proc/pressure/memoryのsome・full(メモリ待ちで止まった時間の割合)、ゲームサーバープロセスのpidstat -r majflt/s(ディスクから読み込む必要があったページフォルト)をティック時間と重ねて確認 - 該当する場合:ラグの時刻にsiが0より大きく、ゲームサーバーのmajflt/sとmemoryのfull値が一緒に上がる - 該当しない場合:si・soが0で、メモリプレッシャー(PSI)も0付近ならスワップは原因ではない。スワップがないのにmajflt/sとPSIが上がるなら、メモリが尽きてコードページを読み直している状態なので、メモリの確保が先 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:GCを使うサーバーは回収時にヒープのあちこちを読むため、ヒープの一部がスワップされただけでも1回のGCが数秒〜数十秒に延びることがあります。スワップを無効にすると、スワップで遅くなる段階を経ずに強制終了(OOM)に進むので、空きメモリを先に確保しておく必要があります。スワップがなくても、メモリが底をつきかけるとOSが実行ファイルのコードページまでメモリから追い出しては読み直すため、強制終了の前にしばらくサーバー全体がひどく遅くなることがあります。 - 出典: - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · 「Numbers Everyone Should Know」:メインメモリ参照100ns、ディスクシーク10ms(2009年時点) - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · swappiness:スワップとファイルページ回収の相対コスト、スワップはランダムI/Oなので高コスト - [Concepts overview](https://docs.kernel.org/admin-guide/mm/concepts.html) · Linux kernel · ディスクに元データがあるページキャッシュとスワップ可能なページを回収し、それでも足りなければOOM Killerがプロセスを強制終了 - [Solidigm™ D7-P5520 and D7-P5620 Product Brief](https://www.solidigm.com/products/data-center/product-briefs/d7-p5520-p5620-product-brief.html) · Solidigm · サーバー向けNVMe SSDの99.99パーセンタイル遅延(four-nines latency)130µs:SSDの1回の読み出しが100µs前後という根拠 - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · クラウドの標準ディスク(gp3)の遅延は1桁ms台 - [vmstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/vmstat.8.html) · procps-ng · si:1秒あたりにスワップから読み込んだメモリ、so:1秒あたりにスワップへ書き出したメモリ - [PSI - Pressure Stall Information](https://docs.kernel.org/accounting/psi.html) · Linux kernel · /proc/pressure/memoryのsome(一部のタスクが止まっていた時間の割合)とfull(すべてのタスクが同時に止まっていた時間の割合) - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -r:majflt/s(ディスクからページを読み込む必要があったフォルトの数) #### mem-cache-miss · キャッシュミス · CPU cache misses データがメモリのあちこちに散らばっていると、CPUが毎回遅いRAMまで取りに行って待つことになります。 - なぜ → すると → 画面では:オブジェクトがポインターでつながって散らばり、順不同にアクセス → CPUキャッシュにないので毎回RAMから読む(100倍前後遅い) → 同じ処理でもティックのコストが数倍、ひどいとスローモーション - 症状:スローモーション / 要因:ストール - 誰に:サーバー全体 / いつ:常に, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:よく一緒に使うデータを連続して配置(データ指向設計)。 - グラフでは:最初から常に高い(ティック時間、CPU使用率) - 確認箇所:ゲームサーバープロセスにperf stat -d -p PIDをかけて、サイクルあたりの命令数(insn per cycle)とL1・LLCのキャッシュミスを計測し、ティック時間・CPU使用率と合わせて確認 - 該当する場合:CPUはずっと忙しいのにinsn per cycleが低く、LLCミスが多い。データ配置を変えたビルドで、同じ人数でのティック時間が大きく減れば確定 - 該当しない場合:CPU使用率が低いのにティックが遅いなら、ロック・I/O待ちのようにCPUの外で待っている原因 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · L1キャッシュ0.5ns、L2キャッシュ7ns、メインメモリ100ns(2009年時点):RAMまで行くとキャッシュより1〜2桁遅い - [perf-stat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-stat.1.html) · perf · -pで実行中のプロセスのハードウェアイベントを数えてinsn per cycleを表示、-dはL1・LLCのデータキャッシュイベントを追加 #### mem-fragment · メモリの断片化 · Heap fragmentation アロケーションと解放を繰り返して空き領域が細かく分断されると、実際に使っている量よりはるかに多くのメモリを占有するようになります。 - なぜ → すると → 画面では:サイズがまちまちのメモリを、複数のスレッドが長期間アロケーション・解放 → 空き領域が細かく散らばってOSに返せず、使用量がリークのように増え続ける → 長く稼働するほどスワップ・メモリ不足で遅くなり、最後は強制終了 - 症状:スローモーション, 切断 / 要因:ストール - 誰に:サーバー全体 / いつ:長時間稼働するほど - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:サイズ別のメモリプール、断片化に強いアロケーター(jemalloc、mimallocなど)。 - グラフでは:徐々に上昇(プロセスのメモリ(RSS)) - 確認箇所:同じビルドのサーバーを2つ起動し、片方だけ環境変数MALLOC_ARENA_MAXでglibcのアリーナ数を減らすか、jemallocなど別のアロケーターに替えて、数日間pidstat -rのRSSを比較 - 該当する場合:人数・オブジェクト数はほぼ同じなのに、変更したサーバーだけRSSの増加が止まるか大きく減る - 該当しない場合:アロケーターを替えても同じように上がるなら、解放されないメモリ(mem-leak) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:リークと同じ形になるため、ヒープを解析してもリーク箇所は見つかりません。Linuxの標準アロケーター(glibc)はスレッドの多いサーバーで特にひどく、アロケーターを替えるだけで使用量が大きく減ることもあります。 - 出典: - [mallopt(3) — Linux manual page](https://man7.org/linux/man-pages/man3/mallopt.3.html) · Linux man-pages · glibc mallocはスレッド競合を減らすためにアリーナをCPU数の倍数まで作成し、アリーナが多いほどメモリ使用量が増える(M_ARENA_MAXで制限、環境変数MALLOC_ARENA_MAXでも設定可能) - [jemalloc memory allocator](https://jemalloc.net/) · jemalloc · 断片化の回避と並行処理でのスケーラビリティを重視した汎用のmalloc実装 - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -r:プロセスごとのRSS(実際にRAM上にあるメモリ) #### mem-numa · NUMAのリモートメモリ · Remote NUMA access CPUが2つあるサーバーで、反対側のCPUにつながったメモリを使うとアクセスが遅くなります。 - なぜ → すると → 画面では:スレッドとメモリが別々のCPUソケットに配置される → メモリアクセスが遅くなる(機器によって1.5〜2倍) → 同じスペックなのにプロセスごとに性能差 - 症状:スローモーション / 要因:ストール - 誰に:サーバー全体 / いつ:常に - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:プロセス・メモリを1つのソケットに固定(numactl)、ソケットが2つならソケットごとにゲームサーバープロセスを分けて配置。 - グラフでは:一部だけ高い(プロセスごとのティック時間、ノードごとのメモリ) - 確認箇所:numastat -p PIDでゲームサーバープロセスのメモリがどのNUMAノードにあるか、numastatのnuma_miss・other_nodeが増えているかを確認し、そのプロセスが動いているCPUのノードと比較 - 該当する場合:遅いプロセスだけ、メモリの大半が自分の動いているCPUとは別のノードにあり、numactlでCPU・メモリを1つのノードに固定して再起動すると差がなくなる - 該当しない場合:速いプロセスとノード配置が同じなのに遅いなら、ノイジーネイバー、CPUスロットリング、そのプロセスの負荷など別の原因 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [What is NUMA?](https://docs.kernel.org/mm/numa.html) · Linux kernel · 同じセルのメモリはより高速で帯域幅も大きく、別の(リモート)セルのメモリはアクセスが遅い - [numactl(8) — Linux manual page](https://man7.org/linux/man-pages/man8/numactl.8.html) · numactl · --cpunodebind・--membindでプロセスのCPUとメモリを特定のNUMAノードに固定 - [numastat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/numastat.8.html) · numactl · numa_miss(希望したノード以外へのアロケーション)・other_node(別のノードで動くプロセスがこのノードにアロケーション)カウンター、-pでプロセスのノードごとのメモリ ### L11 ディスク(原因9件) #### dk-sync-log · 同期ログ書き込み · Synchronous logging ゲームスレッドがログを1行書くたびにディスクへの書き込み完了を待っていると、ディスクが忙しいときにゲームの進行も一緒に止まります。 - なぜ → すると → 画面では:戦闘・取引ログをゲームスレッドから直接ファイルに書く → 確実な書き込み(fsync)を要求したり、OSの書き込みバッファ(ページキャッシュ)が上限に達したりすると、ディスクが忙しいときに1回の書き込みが数十ms → ログの多い戦闘で一瞬止まる - 症状:カクつき, フリーズ / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:非同期ロギング(メモリバッファ + 別スレッド)、ログ量の削減、ゲームスレッドではfsyncしない。 - インフラチームの対応:ログのローテーション・圧縮はI/O優先度を下げて実行、ログはデータとは別のディスクに、ディスク書き込み遅延の監視。 - グラフでは:不定期なスパイク(サーバーのティック時間、ディスク書き込み遅延) - 確認箇所:iostat -x 1のw_await・aqu-szをティック時間と重ねて確認し、perf trace -p PID --duration 10でゲームサーバー内の10msを超えたwrite・fsync呼び出しとそのスレッドを探す - 該当する場合:ティックが跳ねた時刻に、ゲームスレッドのwrite・fsync呼び出しが数十msかかり、その瞬間ディスク書き込み遅延も跳ね上がる。ログのローテーション・圧縮の時刻と重なることが多い - 該当しない場合:ゲームスレッドに時間のかかったシステムコールがないのにティックが跳ねるなら、GC・ロック・ティックバジェット超過など別の原因。ログ専用スレッドだけが長くかかっているなら、ゲームの進行には影響しない - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:通常、OSは書き込みをまずメモリ(ページキャッシュ)で受け取り、あとからディスクに書き出すので、ログ1行の書き込みはたいていすぐに終わります。止まるのは、fsyncで確実な書き込みを要求したとき、たまった書き込みが上限を超えてOSが書き込みの呼び出しをブロックしたとき、ログファイルをローテーションしたり圧縮したりするときです。そのため、普段は問題ないのに、ディスクが忙しい瞬間にだけ跳ねます。 - 出典: - [fsync(2) — Linux manual page](https://man7.org/linux/man-pages/man2/fsync.2.html) · Linux man-pages · fsyncは変更されたデータをディスク(ディスクキャッシュを含む)まで書き出し、デバイスが完了を通知するまでブロック - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · たまった書き込み(dirty)がdirty_ratioに達すると、書き込みをしているプロセス自身がディスクへの書き出しを肩代わりする - [ionice(1) — Linux manual page](https://man7.org/linux/man-pages/man1/ionice.1.html) · util-linux · idleのI/O優先度で動かした処理は、他のプログラムがディスクを使っていないときだけディスク時間を得る - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x:w_await(書き込み要求がキューで待った時間を含む平均処理時間)、aqu-sz(平均キュー長、旧名avgqu-sz) - [perf-trace(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-trace.1.html) · perf · -pで実行中のプロセスのシステムコールをトレース、--durationで指定したmsより長くかかった呼び出しだけを表示 #### dk-fsync · fsyncの集中 · fsync storms データを「確実に」ディスクに書き込むよう要求すると、ディスクによっては1回に0.1ms〜数十msかかり、要求が集中するとキューが長くなります。 - なぜ → すると → 画面では:定期保存・ログアウトラッシュで確実な書き込みの要求が集中 → ディスクのキューが長くなる → 保存のタイミングごとにラグ、ログアウト・チャンネル移動の遅延 - 症状:カクつき, 入力遅延 / 要因:ストール, 遅延 - 誰に:サーバー全体 / いつ:一定の周期で, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ, インフラチーム・DBインフラ - ゲーム開発チームの対応:保存をまとめて一度に(複数の保存を1回のfsyncで)、保存時刻の分散。 - インフラチームの対応:サーバー機器・OS:電源保護機能のあるサーバー向けSSD、ディスクのキュー長・fsync遅延の監視。DBサーバー:保存先がDBなら、DBのログディスクも同様のSSDに、コミット遅延の監視。 - 数値の目安:1回にかかる時間は機器によって異なりますが、おおよそサーバー向けSSD(電源保護機能付き)で0.1ms、一般的なSSDで1〜数ms、クラウドディスクで1〜2ms、HDDで10ms以上です。1つのスレッドが1回ずつ待つと、HDDでは1秒に100回もこなせません。 - グラフでは:周期的なスパイク(ディスクのキュー長、フラッシュ・書き込み遅延) - 確認箇所:iostat -x 1のf/s・f_await(ディスクが処理したフラッシュの数とかかった時間)、w/s・aqu-sz・w_awaitを定期保存・ログアウトの時刻と重ねて確認。古いsysstatはaqu-szをavgqu-szと表示する。クラウドディスクはEBSのVolumeQueueLength・VolumeAvgWriteLatencyを確認 - 該当する場合:保存時刻・ログアウトラッシュのたびにフラッシュ数とキュー長が一緒に跳ね上がり、w_await・f_awaitが普段の数倍になる。そのとき保存・チャンネル移動が遅れる - 該当しない場合:保存・ログアウトと関係ない時刻にキューが跳ね上がるなら、バックアップ・圧縮(dk-backup)かIOPS上限(dk-iops)。フラッシュ数は変わらないのに遅くなるなら、バーストクレジットの枯渇(dk-burst)側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [fsync(2) — Linux manual page](https://man7.org/linux/man-pages/man2/fsync.2.html) · Linux man-pages · fsyncはディスクキャッシュまでフラッシュし、デバイスが完了を通知するまでブロック - [Reliability (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-reliability.html) · PostgreSQL · 一般的なSATAディスクと多くのSSDは停電で消える書き込みキャッシュを持つため、確実な書き込みにはバッテリー・電源保護付きのキャッシュが必要 - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · クラウドの標準ディスク(gp3)の遅延は1桁ms台、io2 Block Expressは16KiBのI/Oで平均500µs未満 - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · HDDのシーク1回で10ms - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x:f/s・f_await(ディスクが処理したフラッシュ要求の数と平均時間)、w/s、w_await、aqu-sz(旧名avgqu-sz) - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · VolumeQueueLength(完了待ちの要求数)、VolumeAvgWriteLatency(1分平均の書き込み遅延、Nitroインスタンス) #### dk-burst · クラウドディスクのバーストクレジット枯渇 · Burst credit depletion 一部のクラウドディスクや小さなサーバースペックには、一時的にベースラインより速く使えるバーストクレジットがあります。忙しい時間が長引いてクレジットが底をつくと、速度が急に落ちます。 - なぜ → すると → 画面では:ベースライン性能を超えて長時間使用 → バーストクレジットが底をつき、ベースライン性能まで急落 → 毎晩、数時間たったころからラグが出始める - 症状:カクつき, スローモーション, 入力遅延 / 要因:ストール, 遅延 - 誰に:サーバー全体 / いつ:夜のピーク時間帯, 長時間稼働するほど - 主担当:インフラチーム・サーバーインフラ / 副担当:インフラチーム・DBインフラ - インフラチームの対応:サーバー機器・OS:性能保証型のディスク(gp3、IOPS指定型)、クレジット残高のアラート、インスタンスのディスク帯域幅のバースト上限とCPUクレジットも合わせて確認。DBサーバー:マネージドDBを含むDBのディスクも性能保証型に変更し、クレジット残高のアラート。 - 数値の目安:AWSのgp2 100GBディスクは通常300、バースト時3,000 IOPSで、クレジットが満タンなら約30分持ちます。gp3はクレジットなしで常に3,000です。Azureの小さなPremium SSDも、クレジットで最大30分バーストします。 - グラフでは:上限で頭打ち(IOPS、バーストクレジット残高) - 確認箇所:CloudWatchのEBS BurstBalance(gp2・st1・sc1)、インスタンスのEBSIOBalance%・EBSByteBalance%(バーストする一部のインスタンス)、バースト可能インスタンスのCPUCreditBalanceを確認。AzureはData Disk Used Burst IO Credits Percentageなど、バーストクレジット使用率のメトリクスを確認 - 該当する場合:残高が0近くまで下がった時刻から、IOPS(VolumeReadOps・VolumeWriteOps)がベースライン性能で頭打ちになり、VolumeQueueLengthとラグが一緒に増える。ピークが数時間続いた後に始まる - 該当しない場合:残高がどれも十分なのにIOPSが頭打ちなら、ボリューム・インスタンスの固定上限(dk-iops) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:ディスク自体に問題がなくても、小さな仮想サーバーはインスタンスのディスク帯域幅そのものにバースト上限(1日最低30分など)があり、同じ形になります。CPUクレジットを使う低価格のサーバーも、クレジットが底をつくとベースライン性能まで遅くなります。 - 出典: - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · gp2のベースライン性能はGiBあたり3 IOPS(最小100)、I/Oクレジットで3,000 IOPSまでバースト、540万クレジットで最低30分。gp3はバーストなしで常に3,000 IOPS - [Managed disk bursting](https://learn.microsoft.com/en-us/azure/virtual-machines/disk-bursting) · Microsoft Azure · Premium SSD P20以下はクレジットベースのバースト、クレジットが満タンなら最大バースト速度で30分 - [Amazon EBS-optimized instance types](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-optimized.html) · AWS · 一部のインスタンスはEBSの最大性能を24時間に1回、30分だけ維持し、その後ベースライン性能に戻る - [Standard mode for burstable performance instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-standard-mode.html) · AWS · 標準モードのバースト可能インスタンスは、CPUクレジットが尽きるとCPU使用率をベースラインまで(急落しないよう徐々に)下げる - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · BurstBalance:gp2のI/Oクレジット、st1・sc1のスループットクレジットの残高(%)、VolumeReadOps・VolumeWriteOps・VolumeQueueLength - [CloudWatch metrics that are available for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · EBSIOBalance%・EBSByteBalance%:24時間に1回30分バーストする一部インスタンスのEBSクレジット残高、CPUCreditBalance:バースト可能インスタンスのCPUクレジット残高 - [Disk metrics](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-metrics) · Microsoft Azure · Data Disk Used Burst IO Credits Percentageなど、ディスク・VMのバーストクレジット使用率(5分間隔) #### dk-iops · IOPS上限・キューの飽和 · IOPS limit / queue saturation ディスクが1秒間に処理できる要求数を超えると、キューが長くなって遅延が急増します。 - なぜ → すると → 画面では:読み書きの要求がディスクの処理能力に近づく → キューが長くなる(たいてい利用率90%以上で急増) → 保存・ロードの遅延、同期呼び出しならフリーズ - 症状:入力遅延, フリーズ / 要因:遅延, ストール - 誰に:サーバー全体 / いつ:人が集中したとき, 夜のピーク時間帯 - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発, インフラチーム・DBインフラ - ゲーム開発チームの対応:要求をまとめる、よく読むデータはキャッシュ、ゲームスレッドでディスクを待たないよう非同期に。 - インフラチームの対応:サーバー機器・OS:より高速なディスク、インスタンスタイプごとのディスク帯域幅・IOPS上限の確認、ディスク利用率・キューのアラート、大きなファイルのコピーは空いている時間帯に。DBサーバー:DBのディスクもIOPS・スループット利用率のアラート、DBインスタンスのスペックごとのディスク上限の確認。 - 数値の目安:HDDは約150、SATA SSDは数万、NVMeは数十万IOPS。クラウドの標準ディスク(AWS gp3)は3,000 IOPS、毎秒125MiBです。IOPSとは別にある毎秒の転送量の上限を大きなファイルのコピーが使い切ると、小さな書き込みまで詰まります。 - グラフでは:上限で頭打ち(IOPS、ディスクのキュー長) - 確認箇所:iostat -x 1のr/s・w/s、rkB/s・wkB/s、aqu-sz、r_await・w_awaitを確認。クラウドはEBSのVolumeReadOps・VolumeWriteOps・VolumeQueueLengthと、上限超過の有無を示すVolumeIOPSExceededCheck・VolumeThroughputExceededCheck、インスタンス側のInstanceEBSIOPSExceededCheck・InstanceEBSThroughputExceededCheckを確認 - 該当する場合:毎秒の要求数や転送量が上限値で頭打ちになり、aqu-szとawaitが一緒に跳ね上がる。クラウドでは超過チェックのメトリクスが1 - 該当しない場合:%utilが100%でもawaitが低ければ、まだ余裕があることもある。要求を並列に処理するSSD・RAIDでは、%utilは上限を意味しない。上限に達していないのにawaitだけが高いなら、ディスク自体の遅延(dk-hdd)かfsync(dk-fsync) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:クラウドでは、ディスクの上限とは別に、サーバースペック(インスタンスタイプ)ごとにディスク帯域幅・IOPSの上限があります。高価なディスクを付けても、サーバーが小さければインスタンスの上限で頭打ちになります。 - 出典: - [Exos X18 Data Sheet](https://www.seagate.com/www-content/datasheets/pdfs/exos-x18-mango-DS2045-1N-2007US-en_US.pdf) · Seagate · 7,200rpmのサーバー向けHDDの4Kランダム読み出し170 IOPS(QD16) - [D3-S4520 SSD](https://www.solidigm.com/products/data-center/d3/s4520.html) · Solidigm · サーバー向けSATA SSDの4KBランダム読み出し・書き込み、最大92K/48K IOPS - [Solidigm™ D7-P5520 and D7-P5620 Product Brief](https://www.solidigm.com/products/data-center/product-briefs/d7-p5520-p5620-product-brief.html) · Solidigm · サーバー向けNVMe SSDのランダム読み出し・書き込み、1,000K/200K IOPS - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · gp3のベースライン性能は3,000 IOPSと125MiB/s、2つは別々に引き上げられる独立した上限 - [Amazon EBS-optimized instance types](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-optimized.html) · AWS · インスタンスタイプごとに、EBSの帯域幅・スループット・IOPSのベースラインと最大の上限が別にある - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x:r/s・w/s、rkB/s・wkB/s、aqu-sz(旧名avgqu-sz)、r_await・w_await、%util。要求を並列に処理するRAID・最近のSSDでは、%utilは性能の上限を表さない - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · VolumeIOPSExceededCheck・VolumeThroughputExceededCheck:ボリュームのIOPS・スループット上限を超えようとしたら1(Nitroインスタンス)、VolumeQueueLength - [CloudWatch metrics that are available for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · InstanceEBSIOPSExceededCheck・InstanceEBSThroughputExceededCheck:インスタンスのEBS IOPS・スループット上限を超えようとしたら1 #### dk-full · ディスクフル · Disk full ログとダンプがたまってディスクがいっぱいになると書き込みが失敗し、備えがなければサーバーが落ちます。 - なぜ → すると → 画面では:ログ・ダンプ・一時ファイルがたまって100% → 書き込み失敗。エラー処理がなければクラッシュ、あれば保存失敗 → 切断、進行状況のロールバック - 症状:切断, 不発・ロールバック / 要因:ストール - 誰に:サーバー全体 / いつ:長時間稼働するほど - 主担当:インフラチーム・サーバーインフラ / 副担当:インフラチーム・DBインフラ, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:書き込み失敗を処理し、クラッシュさせる代わりに保存の再試行とアラート、不要なログ・ダンプの削減。 - インフラチームの対応:サーバー機器・OS:ログのローテーション、容量のアラート、ログとデータのディスク分離。DBサーバー:DBのトランザクションログ(WAL、binlog)が、レプリケーションの停止・ログバックアップの漏れでたまっていないか監視。 - グラフでは:徐々に上昇(ディスク使用率) - 確認箇所:df -hの使用率とdf -iのinode使用率を確認し、サーバー・DBのログからENOSPCエラーを探す。DBは、PostgreSQLのpg_replication_slotsでactiveがfalseのスロット、MySQLのSHOW BINARY LOGSのファイル数・サイズ、SQL Serverのsys.databasesのlog_reuse_wait_desc、RDSはFreeStorageSpaceを確認 - 該当する場合:使用率が数日かけて着実に上がって100%に達した時刻と、クラッシュ・保存失敗の時刻が重なり、ログにENOSPCが残る - 該当しない場合:空き容量が十分なのに書き込みが失敗するなら、権限・ファイルサイズ上限など別の原因 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:DBのトランザクションログ(WAL、binlogなど)は、レプリカが止まったりログのバックアップが抜けたりすると、削除されずにたまり続けます。このディスクがいっぱいになるとDBのすべての書き込みが止まり、保存・取引が一斉に失敗します。 - 出典: - [write(2) — Linux manual page](https://man7.org/linux/man-pages/man2/write.2.html) · Linux man-pages · デバイスに空きがないと、書き込みがENOSPCエラーで失敗 - [Monitoring Disk Usage (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/diskusage.html) · PostgreSQL · WALディスクがいっぱいになると、DBサーバーがパニックで停止することがある - [Log-Shipping Standby Servers (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/warm-standby.html) · PostgreSQL · レプリケーションスロットはレプリカが受け取るまでWALを削除しないため、pg_walの領域を埋め尽くすことがある(max_slot_wal_keep_sizeで制限) - [Troubleshoot a full transaction log (SQL Server Error 9002)](https://learn.microsoft.com/en-us/sql/relational-databases/logs/troubleshoot-a-full-transaction-log-sql-server-error-9002) · Microsoft SQL Server · ログがいっぱいになるとDBは読み取りのみで更新不可、ログバックアップの漏れ・レプリケーション遅延・長いトランザクションがログの再利用を妨げるよくある原因、何が妨げているかはsys.databasesのlog_reuse_wait_descで確認 - [df(1) — Linux manual page](https://man7.org/linux/man-pages/man1/df.1.html) · coreutils · ファイルシステムごとの使用量、-iはブロックの代わりにinodeの使用量 - [pg_replication_slots (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/view-pg-replication-slots.html) · PostgreSQL · active:現在ストリーミング中のスロットか、wal_status:スロットが保持しているWALがmax_wal_sizeを超えたか - [SHOW BINARY LOGS Statement](https://dev.mysql.com/doc/refman/8.4/en/show-binary-logs.html) · MySQL · サーバーのバイナリログファイルの一覧とファイルサイズ(File_size) - [Amazon CloudWatch metrics for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) · AWS · FreeStorageSpace:DBインスタンスの残りのストレージ容量 #### dk-backup · バックアップ・圧縮・スキャン処理 · Backup / compression / scans 深夜のバックアップ、ログの圧縮、セキュリティスキャンがディスクを独占すると、ゲームサーバーの読み書きが詰まります。 - なぜ → すると → 画面では:スケジュールされたバックアップ・圧縮処理が始まる → ディスクの帯域幅とIOPSの大半を占有 → 毎日同じ時刻にラグ - 症状:カクつき, 入力遅延 / 要因:ストール, 遅延 - 誰に:サーバー全体 / いつ:一定の周期で - 主担当:インフラチーム・サーバーインフラ / 副担当:インフラチーム・DBインフラ - インフラチームの対応:サーバー機器・OS:バックアップ・圧縮・スキャン処理のI/O優先度を下げる、時刻の分散。DBサーバー:バックアップはレプリカで。 - グラフでは:周期的なスパイク(ディスク利用率、ディスク待ち時間) - 確認箇所:sar -dで過去数日の記録(/var/log/saの日別ファイル。sadcが-S DISKでディスク項目まで収集している必要あり)の%util・await・aqu-szを日付ごとに重ねて確認し、その時刻にpidstat -d 1でkB_rd/s・kB_wr/sが最も大きいプロセスを探して、cron・systemdタイマーのスケジュールと照合 - 該当する場合:毎日同じ時刻にawaitと%utilが跳ね上がり、そのときバックアップ・圧縮・スキャンのプロセスがディスク読み書きの大半を占める - 該当しない場合:跳ね上がる時刻が日によって違うなら、スケジュールされた処理の可能性は低い。その時刻のI/Oの大半がゲームサーバー自身なら、保存・ログ側(dk-fsync、dk-sync-log) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [ionice(1) — Linux manual page](https://man7.org/linux/man-pages/man1/ionice.1.html) · util-linux · idleクラスで動かした処理は、他のプログラムがディスクを使っていないときだけディスク時間を得る - [Using Replication for Backups](https://dev.mysql.com/doc/refman/8.4/en/replication-solutions-backups.html) · MySQL · レプリカを止めてバックアップしても、プライマリの運用には影響しない - [sar(1) — Linux manual page](https://man7.org/linux/man-pages/man1/sar.1.html) · sysstat · -d:日別の記録ファイル(デフォルトは/var/log/sa)のデバイスごとのawait・aqu-sz・%util、ディスク項目はsadcの-S DISKオプションで収集する必要あり - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -d:プロセスごとのkB_rd/s・kB_wr/s(1秒あたりのディスク読み込み量・書き込み量) #### dk-lazy-load · サーバーの遅延ロード · Lazy loading on the server サーバーがダンジョン・マップのデータを初めて要求されたときにディスクから読むと、そのティックの間、全員が止まります。 - なぜ → すると → 画面では:誰かが初めてダンジョン・エリアに入場 → サーバーがゲームスレッドでデータをディスクから読む → そのサーバーの全員が一瞬フリーズ - 症状:フリーズ / 要因:ストール - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:サーバー起動時の事前ロード、非同期ロード。 - インフラチームの対応:スナップショットから作ったばかりのサーバーは、サービス投入前にディスクのウォームアップ(全ブロックを一度読む)を行うか、高速スナップショット復元機能を使う。 - グラフでは:不定期なスパイク(サーバーのティック時間、ディスク読み込み) - 確認箇所:止まった時刻をゲームサーバーログのダンジョン・エリアへの初回入場の記録と突き合わせ、その瞬間のゲームサーバーのディスク読み込み(pidstat -dのkB_rd/s)と、perf trace --durationで長くかかったread・open呼び出しを確認。新しく立ち上がったクラウドサーバーなら、EBSのVolumeAvgReadLatencyを古いサーバーと比較 - 該当する場合:初めて入場した瞬間だけ止まり、同じ場所に2回目に入るときは止まらない。止まっている間、ゲームスレッドがファイルの読み込みで待っている - 該当しない場合:すでにロード済みのエリアでも同じように止まるなら、ティックバジェット超過・GCなど別の原因 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:クラウドでスナップショット(ディスクのコピー)から作ったばかりのサーバーは、初めて読むブロックごとにリモートストレージから取得するため、普段よりはるかに遅くなります。オートスケーリングで新しく立ち上がったサーバーでだけ最初の入場が際立って長くかかるなら、これを疑います。 - 出典: - [Initialize Amazon EBS volumes](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-initialize.html) · AWS · スナップショットから作ったボリュームは、ブロックをS3から取得している間は遅延が増えて性能が落ちる、dd・fioで全ブロックを読んで事前に初期化 - [Amazon EBS fast snapshot restore](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-fast-snapshot-restore.html) · AWS · 高速スナップショット復元は、作成時から初期化済みのボリュームを提供し、初回アクセスの遅延をなくす - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -d:プロセスごとのkB_rd/s(1秒あたりのディスク読み込み量) - [perf-trace(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-trace.1.html) · perf · --durationで指定したmsより長くかかったシステムコールだけを表示 - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · VolumeAvgReadLatency:1分平均の読み込み遅延(Nitroインスタンス) #### dk-coredump · コアダンプの書き出し · Core dump writing サーバーが落ちるときに数GBのメモリをディスクに書き出すため、再起動が数分遅れることもあります。 - なぜ → すると → 画面では:サーバーのクラッシュでメモリ全体をファイルに書き出す → 数GBを書き込む間は再起動できない → サーバーが落ちて切断された後、しばらく接続できない - 症状:接続不可・無限ロード / 要因:ストール - 誰に:サーバー全体 / いつ:ときどきランダムに - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:必要なメモリだけを含む小さなダンプ(ミニダンプ)方式の検討、クラッシュ原因の修正。 - インフラチームの対応:ダンプサイズの制限(OSのコアダンプ設定)、高速なディスク、再起動とダンプの分離(ダンプの圧縮・アップロードは再起動後に別途処理)。 - グラフでは:接続が一斉に切れる(接続数、サーバーの再起動時刻) - 確認箇所:クラッシュ時刻とコアファイルのサイズ(coredumpctl list・info、またはcore_patternが指す場所のファイル)、ファイルの書き込みが終わった時刻、サービスが再び立ち上がった時刻を並べ、その間のiostat -xのwkB/sを確認 - 該当する場合:クラッシュ後、数GBのコアファイルを書き込む間ディスク書き込みが上限近くに張り付き、書き出しが終わってから再起動が始まる - 該当しない場合:コアダンプが無効か小さく済んだのに再起動が遅いなら、マップのロードやDBのコールドキャッシュ(db-cold-cache)など、サーバーの起動処理側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [core(5) — Linux manual page](https://man7.org/linux/man-pages/man5/core.5.html) · Linux man-pages · RLIMIT_COREでコアファイルサイズの上限、coredump_filterで含めるメモリ領域を選択、コアダンプをプログラムにパイプして別途処理 - [Minidump Files](https://learn.microsoft.com/en-us/windows/win32/debug/minidump-files) · Microsoft · ミニダンプはクラッシュダンプ情報のうち有用な一部だけを含み、速く小さく作れる - [coredumpctl(1) — Linux manual page](https://man7.org/linux/man-pages/man1/coredumpctl.1.html) · systemd · list:ジャーナルに残ったコアダンプの一覧(TIMEはカーネルが通知したクラッシュ時刻)、info:ダンプごとの詳細とディスクに書いたサイズ - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x:wkB/s(1秒あたりのディスク書き込み量) #### dk-hdd · HDDのシーク遅延 · HDD seek latency HDDはヘッドがプラッタ上を移動する(シーク、seek)必要があるため、散らばったデータの読み書きに1回あたり10ms近くかかります。 - なぜ → すると → 画面では:古いサーバーや低価格ストレージでHDDを使用 → ランダムな読み書きのたびに約10ms → 全体的な保存・ロードの遅延 - 症状:入力遅延 / 要因:遅延 - 誰に:サーバー全体 / いつ:常に - 主担当:インフラチーム・サーバーインフラ / 副担当:インフラチーム・DBインフラ, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:シーケンシャル書き込み中心の設計。 - インフラチームの対応:サーバー機器・OS:SSDに交換(ランダムな読み書きが多い保存用ディスクから)。DBサーバー:ランダムな読み書きが最も多いDBディスクを先にSSDに交換。 - グラフでは:最初から常に高い(ディスクの読み書き遅延(r_await・w_await)) - 確認箇所:lsblk -d -o NAME,ROTAで回転ディスク(HDD)かどうかを確認し、iostat -x 1のr/s・w/sとr_await・w_awaitを確認。仮想サーバーはクラウド・ストレージの仕様でディスクの種類を確認 - 該当する場合:回転ディスクで、毎秒の要求が数十〜100件あまりの水準なのに、r_await・w_awaitが常に数ms〜数十ms - 該当しない場合:SSDなのに遅延が高いなら、キューの飽和(dk-iops)かバーストクレジットの枯渇(dk-burst)側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · ディスクシーク1回10ms、ディスクからの1MBシーケンシャル読み出し20ms - [Exos X18 Data Sheet](https://www.seagate.com/www-content/datasheets/pdfs/exos-x18-mango-DS2045-1N-2007US-en_US.pdf) · Seagate · 7,200rpm HDDの平均回転待ち4.16ms、4Kランダム読み出し170 IOPS - [lsblk(8) — Linux manual page](https://man7.org/linux/man-pages/man8/lsblk.8.html) · util-linux · -oで出力する列を選択、デバイストポロジーの列にROTA(回転型かどうか)を含む - [ABI stable symbols](https://docs.kernel.org/admin-guide/abi-stable.html) · Linux kernel · /sys/block/(ディスク)/queue/rotational:デバイスが回転型か非回転型かを示す - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x:r/s・w/s、r_await・w_await(キューで待った時間を含む、要求あたりの平均処理時間) ### L12 データベース(原因16件) #### db-no-index · インデックスのないクエリ · Missing index / full table scan インデックスがないと、条件に合う行を探すためにテーブル全体を読む必要があります(フルスキャン)。 - なぜ → すると → 画面では:新機能のデプロイで、インデックスのない条件検索が追加される → 数百万行をすべてスキャンし、クエリ1つに数百ms〜数秒 → 郵便受け・取引履歴のロード遅延、コネクションがふさがって他の要求まで待たされる - 症状:入力遅延, 接続不可・無限ロード / 要因:遅延, ストール - 誰に:特定の機能だけ, サーバー全体 / いつ:特定の操作をしたとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:新しいクエリはデプロイ前に実行計画をレビュー、インデックスの追加、更新系のクエリ(UPDATE・DELETE)もインデックスが効くか確認。 - インフラチームの対応:スロークエリログの監視、フルスキャンのクエリを見つけてゲーム開発チームに共有、運用中のインデックス追加はロックの短いオンライン方式で適用。 - 数値の目安:インデックスがあれば数ms、なければデータ量に比例して遅くなり、大きなテーブルでは数百〜数万倍。 - グラフでは:ある時点から階段状に上昇(DBクエリの遅延、読み取った行数) - 確認箇所:MySQLはslow query log(log_queries_not_using_indexesを有効にすると、インデックスを使わなかったクエリも記録)のRows_examined・Rows_sent、performance_schemaのevents_statements_summary_by_digestのSUM_NO_INDEX_USED・SUM_ROWS_EXAMINEDを確認し、EXPLAINを実行。PostgreSQLはpg_stat_user_tablesのseq_scan・seq_tup_readを確認し、EXPLAINを実行 - 該当する場合:デプロイ後に新しく現れたクエリが、返した行(Rows_sent)より数千倍多い行を読み(Rows_examined)、EXPLAINにテーブルのフルスキャン(MySQLはtype ALL、PostgreSQLはSeq Scan)が出る。大きなテーブルのseq_tup_readがデプロイ時刻から急激に増える - 該当しない場合:インデックスが効いているのに遅いなら、ロック待ち(db-hot-row、db-ddl-lock)か実行計画の変化(db-plan-flip)。小さなテーブルのフルスキャンは正常なこともある - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:影響は読み込みが遅くなるだけにとどまりません。インデックスなしの更新系クエリ(UPDATE・DELETE)は、DBによってはスキャンした行までロックし、無関係なプレイヤーの保存まで止めてしまうことがあります。 - 出典: - [How MySQL Uses Indexes](https://dev.mysql.com/doc/refman/8.4/en/mysql-indexes.html) · MySQL · インデックスがないと先頭行からテーブル全体を読み、テーブルが大きいほどコストが増える - [Locks Set by Different SQL Statements in InnoDB](https://dev.mysql.com/doc/refman/8.4/en/innodb-locks-set.html) · MySQL · 適切なインデックスがなくテーブル全体をスキャンすると、すべての行がロックされ、他のユーザーの挿入まで止まる - [The Slow Query Log](https://dev.mysql.com/doc/refman/8.4/en/slow-query-log.html) · MySQL · long_query_time(デフォルト10秒)を超えたクエリを記録、インデックスを使わないクエリも別途記録可能 - [CREATE INDEX (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/sql-createindex.html) · PostgreSQL · CONCURRENTLYで作成すると書き込みを止めずにインデックスを作成、通常の作成は終わるまで書き込みを止める - [Statement Summary Tables](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-statement-summary-tables.html) · MySQL · events_statements_summary_by_digest:同じ形のクエリごとのSUM_NO_INDEX_USED(インデックスなしで実行した回数)・SUM_ROWS_EXAMINED - [EXPLAIN Output Format](https://dev.mysql.com/doc/refman/8.4/en/explain-output.html) · MySQL · typeがALLならテーブルのフルスキャン、通常はインデックスを追加して回避 - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_user_tablesのseq_scan(シーケンシャルスキャンの回数)・seq_tup_read(シーケンシャルスキャンで読んだ行数) - [Using EXPLAIN (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/using-explain.html) · PostgreSQL · Seq Scan:テーブルのすべての行を順に読む実行計画 #### db-hot-row · ホットスポットの行ロック競合 · Hot row lock contention 全員が同じ行(ギルド倉庫、オークションの人気アイテム、サーバー全体のカウンター)を更新しようとすると、ロックを取れるのは1人ずつです。 - なぜ → すると → 画面では:イベント・人気アイテムで同じ行に更新が集中 → ロックを取れるまで要求が待たされる → 取引失敗、「しばらくしてからもう一度お試しください」、タイムアウト - 症状:不発・ロールバック, 入力遅延 / 要因:ストール, 遅延 - 誰に:特定の機能だけ / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:行の分割(シャーディングしたカウンター)、トランザクションを短く、メモリで集約して一度に反映。 - インフラチームの対応:行ロックの待ち時間・件数を監視し、競合が集中する行を見つけて共有。 - 数値の目安:1つの要求がロックを10ms保持していると、その行は1秒に最大100回しか更新できません。トランザクションの中に別サーバーとの往復が挟まると、その分さらに減ります。 - グラフでは:人数・負荷に連動して上昇(行ロックの待ち数・待ち時間) - 確認箇所:MySQLはInnodb_row_lock_waits・Innodb_row_lock_timeの増加量とInnodb_row_lock_current_waitsを確認し、sys.innodb_lock_waitsで誰が誰を待っているかを探す。PostgreSQLはpg_stat_activityでwait_event_typeがLockのセッション、pg_locksでgrantedがfalseの要求を確認し、log_lock_waits(デフォルト無効)を有効にすると長く待ったロックがログに残る - 該当する場合:イベント・人数に連動してロック待ちが急増し、待っている要求の大半が同じテーブルの同じ行(同じキー)を指している - 該当しない場合:待ちが複数のテーブル・行に均等に散らばっているなら、ディスク・CPUの飽和側。1つのセッションがロックを長く保持して離さないなら、長時間開いたままのトランザクション(db-long-tx) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [InnoDB Locking](https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html) · MySQL · あるトランザクションが行(インデックスレコード)にロックをかけると、他のトランザクションはその行を更新できずに待つ - [How to Minimize and Handle Deadlocks](https://dev.mysql.com/doc/refman/8.4/en/innodb-deadlocks-handling.html) · MySQL · トランザクションを小さく短く保ち、関連する変更の直後にすぐコミットして衝突を減らすよう推奨 - [Server Status Variables](https://dev.mysql.com/doc/refman/8.4/en/server-status-variables.html) · MySQL · Innodb_row_lock_waits・Innodb_row_lock_timeで行ロック待ちの回数と時間、Innodb_row_lock_current_waitsで現在待っている数を確認 - [The innodb_lock_waits and x$innodb_lock_waits Views](https://dev.mysql.com/doc/refman/8.4/en/sys-innodb-lock-waits.html) · MySQL · 待っているクエリ(waiting_query)とブロックしているセッション(blocking_pid)、待ち時間(wait_age) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_activityのwait_event_type:Lockなら重量ロックの待ち - [pg_locks (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/view-pg-locks.html) · PostgreSQL · grantedがfalseなら、そのプロセスがロックを取得しようと待っている - [Error Reporting and Logging (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-logging.html) · PostgreSQL · log_lock_waits:deadlock_timeoutより長くロックを待つとログを出力、デフォルト無効 #### db-deadlock · DBのデッドロック · Database deadlock 2つのトランザクション(ひとまとまりで処理されるDB操作)が互いに相手のロックした行を待つと、DBが片方を強制的に取り消します。 - なぜ → すると → 画面では:取引Aはアイテム→通貨、Bは通貨→アイテムの順にロック → DBがデッドロックを検知して片方をロールバック → 取引・製作がときどき失敗、アイテムが元に戻る - 症状:不発・ロールバック, 入力遅延 / 要因:パケットロス, ストール - 誰に:特定の機能だけ / いつ:人が集中したとき, 特定の操作をしたとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:ロック順序の統一、トランザクションを短く、失敗時の自動再試行。 - インフラチームの対応:デッドロック検知を有効にしておき、デッドロックの記録を収集して共有、検知を無効にしたMySQLサーバーはロック待ちの上限(デフォルト50秒)を短縮。 - 数値の目安:検知までに、MySQL(InnoDB)はほぼ即時、PostgreSQLはデフォルトで1秒、SQL Serverは最大5秒ほどかかります。その間、両方の要求が止まっています。同時要求が非常に多いためにMySQLの検知を無効にしているサーバーでは、ロック待ちの上限(デフォルト50秒)まで待ちます。 - グラフでは:不定期なスパイク(デッドロック数、取引失敗数) - 確認箇所:MySQLはSHOW ENGINE INNODB STATUSのLATEST DETECTED DEADLOCK(直近の1件)、innodb_print_all_deadlocksを有効にするとエラーログに残るすべてのデッドロック、INFORMATION_SCHEMA.INNODB_METRICSのlock_deadlocksを確認。PostgreSQLはpg_stat_databaseのdeadlocks、SQL Serverはデフォルトで有効なsystem_healthセッションのxml_deadlock_reportを確認。ゲームサーバー側のエラーコードはMySQL 1213、PostgreSQL 40P01、SQL Server 1205 - 該当する場合:取引・製作の失敗時刻にデッドロック数が増え、記録された2つのトランザクションが同じテーブル群を互いに逆の順序でロックしている - 該当しない場合:デッドロック数は変わらないのに失敗するなら、ロック待ちの上限超過(MySQLエラー1205)かホットスポット(db-hot-row) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [InnoDB Startup Options and System Variables](https://dev.mysql.com/doc/refman/8.4/en/innodb-parameters.html) · MySQL · 検知が有効なら(デフォルト)InnoDBはデッドロックを即座に検知してロールバック、innodb_lock_wait_timeoutのデフォルトは50秒 - [Deadlock Detection](https://dev.mysql.com/doc/refman/8.4/en/innodb-deadlock-detection.html) · MySQL · 同時実行性が非常に高いと検知自体が遅くなることがあるため、無効にしてロック待ちの上限に任せることもある - [Lock Management (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-locks.html) · PostgreSQL · deadlock_timeoutのデフォルトは1秒:この時間ロックを待ってから初めてデッドロックを検査 - [Deadlocks guide](https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-deadlocks-guide) · Microsoft SQL Server · デッドロック検査のデフォルト間隔は5秒、デッドロックが頻発すると100msまで短縮、デフォルトで有効なsystem_healthセッションがxml_deadlock_reportを収集、犠牲になった側はエラー1205 - [How to Minimize and Handle Deadlocks](https://dev.mysql.com/doc/refman/8.4/en/innodb-deadlocks-handling.html) · MySQL · 複数の行・テーブルは常に同じ順序で更新する、失敗したら再試行、innodb_print_all_deadlocksですべてのデッドロックを記録 - [InnoDB Standard Monitor and Lock Monitor Output](https://dev.mysql.com/doc/refman/8.4/en/innodb-standard-monitor.html) · MySQL · LATEST DETECTED DEADLOCK:直近のデッドロックの2つのトランザクション、保持したロック・待ったロック、ロールバックした側 - [InnoDB INFORMATION_SCHEMA Metrics Table](https://dev.mysql.com/doc/refman/8.4/en/innodb-information-schema-metrics-table.html) · MySQL · INNODB_METRICSのlock_deadlocksカウンター(デフォルトで有効) - [Server Error Message Reference](https://dev.mysql.com/doc/mysql-errors/8.4/en/server-error-reference.html) · MySQL · 1213 ER_LOCK_DEADLOCK(デッドロック)、1205 ER_LOCK_WAIT_TIMEOUT(ロック待ちの上限超過) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_databaseのdeadlocks:このDBで検知したデッドロックの数 - [PostgreSQL Error Codes (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/errcodes-appendix.html) · PostgreSQL · 40P01 deadlock_detected #### db-pool · コネクションプールの枯渇 · Connection pool exhaustion DBとの接続数は決まっているため、遅いクエリが接続を占有すると、残りの要求は待たされます。 - なぜ → すると → 画面では:遅いクエリや要求の殺到で、すべての接続が使用中 → 新しい要求は接続が空くまで待つ → ログインの無限ロード、保存の遅延、タイムアウト - 症状:接続不可・無限ロード, 入力遅延 / 要因:ストール, 遅延 - 誰に:サーバー全体, 特定の機能だけ / いつ:接続直後・メンテ明け, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:遅いクエリの排除、プールサイズと待ちタイムアウトの調整(プールをむやみに大きくしない)、機能別のプール分離。 - インフラチームの対応:DBの最大接続数とCPU・IOPSの余裕を確認、増設・オートスケーリングの前にサーバー台数 × プールサイズが最大接続数以内か確認、コネクション待ち・ロック待ちのメトリクスを監視に追加。 - 数値の目安:必要な接続数は「毎秒の要求数 × 1件が接続を占有する時間」で見積もります。1秒に2,000件、1件あたり5msなら、平均10本が常に使用中です。集中するときに備えて、通常はその2〜3倍を用意します。クエリが150msに遅くなると、同じ要求数で300本が必要になります。 - グラフでは:上限で頭打ち(使用中のDB接続数、コネクション待ち時間) - 確認箇所:DB側でゲームサーバーごとの接続状態を集計。MySQLはSHOW PROCESSLISTのHost・Command(アイドル状態の接続はSleep)・TimeとThreads_connected・Threads_running、拒否された接続数Connection_errors_max_connectionsを確認。PostgreSQLはpg_stat_activityをclient_addr・stateでまとめて集計。ゲームサーバーのコネクションプールライブラリが待ち数・待ち時間を出力していれば、合わせて確認 - 該当する場合:1台のゲームサーバーの接続がプールサイズいっぱいまですべてクエリ実行中で、アイドル状態の接続が0の間、ログイン・保存が待たされる。またはDB全体の接続数がmax_connectionsに達し、新しい接続が拒否される - 該当しない場合:アイドル状態の接続が十分あるのに遅いなら、クエリ自体の遅延(db-no-index、db-hot-row)かDBリソースの飽和側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:プールをむやみに大きくすると、DBのCPUとロック競合が増えるだけで、全員が一緒に遅くなります。また、サーバー台数 × プールサイズがDBの最大接続数を超えると、増設したサーバーや再起動したサーバーが接続すら確立できません。オートスケーリングやメンテ明けによく起きます。 - 出典: - [Number Of Database Connections](https://wiki.postgresql.org/wiki/Number_Of_Database_Connections) · PostgreSQL · DBのリソースを使い切った後は、接続を増やしてもスループットがかえって落ちる、アクティブな接続をリソースに合わせて残りはキューで待たせるほうが、遅延・スループットともに良い - [Too many connections](https://dev.mysql.com/doc/refman/8.4/en/too-many-connections.html) · MySQL · max_connectionsを使い切ると、新しい接続はToo many connectionsエラーで拒否される - [Connections and Authentication (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-connection.html) · PostgreSQL · max_connections:同時接続数の上限、デフォルトは通常100 - [SHOW PROCESSLIST Statement](https://dev.mysql.com/doc/refman/8.4/en/show-processlist.html) · MySQL · Host(クライアントのアドレス)、Command(アイドル状態のセッションはSleep)、Time、State - [Server Status Variables](https://dev.mysql.com/doc/refman/8.4/en/server-status-variables.html) · MySQL · Threads_connected・Threads_running、Connection_errors_max_connections(max_connectionsに達して拒否された接続数) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_activity:接続ごとのclient_addrとstate(active、idle、idle in transactionなど) #### db-replica-lag · レプリケーション遅延 · Replication lag 書き込みはプライマリに、読み込みはレプリカから行う構成で、レプリカの追従が遅れると、書いたばかりの内容が見えません。 - なぜ → すると → 画面では:プライマリに書き込みが集中し、レプリカが数秒遅れる → 保存したばかりの内容をレプリカから読むと、まだ反映されていない → 買ったばかりのアイテムが表示されない、取引所の価格が古い値のまま、重複付与のバグ - 症状:不発・ロールバック / 要因:遅延 - 誰に:特定の機能だけ / いつ:人が集中したとき, 夜のピーク時間帯 - 主担当:インフラチーム・DBインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:書いたばかりのデータはプライマリから読む、付与済みかどうかの確認と付与はプライマリで1つのトランザクションに(ユニークキーや条件付きUPDATEで重複を防止)。 - インフラチームの対応:レプリケーション遅延のアラート、レプリカのスペックをプライマリ以上にして並列レプリケーションを適用、大量削除は細かく分けて実行、レプリカで長時間動く集計クエリの管理。 - グラフでは:人数・負荷に連動して上昇(レプリケーション遅延(秒)) - 確認箇所:MySQLはレプリカでSHOW REPLICA STATUSのSeconds_Behind_Source(8.0.22より前のバージョンはSHOW SLAVE STATUS)を確認。PostgreSQLはプライマリのpg_stat_replicationのwrite_lag・flush_lag・replay_lag、RDSはReplicaLagを確認 - 該当する場合:「表示されない」という報告の時刻に遅延が数秒以上あり、遅延が解消した後に見直すと正常。書き込みの殺到や大量削除、レプリカの長い集計クエリの時刻に遅延が大きくなる - 該当しない場合:遅延が0付近なのに表示されないなら、ゲームサーバーのキャッシュや同期側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:書き込みが多くなくても遅れることがあります。プライマリで10分かかった大量削除が1つあると、レプリカでも再実行される間、その分だけ遅れます。レプリカで長時間動く集計クエリも追従を遅らせます。 - 出典: - [SHOW REPLICA STATUS Statement](https://dev.mysql.com/doc/refman/8.4/en/show-replica-status.html) · MySQL · Seconds_Behind_Source:レプリカが現在適用中のイベントがプライマリで記録された時刻との差(レプリケーション遅延) - [Replica Server Options and Variables](https://dev.mysql.com/doc/refman/8.4/en/replication-options-replica.html) · MySQL · replica_parallel_workersで複数のスレッドがトランザクションを並列に適用(デフォルトは4。0なら1つのスレッドが順番に適用) - [Log-Shipping Standby Servers (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/warm-standby.html) · PostgreSQL · ストリーミングレプリケーションはデフォルトで非同期のため、コミットからレプリカへの反映までに遅延がある(レプリカが追従できていれば通常1秒未満) - [MySQL 8.0 Reference Manual: SHOW REPLICA STATUS Statement](https://dev.mysql.com/doc/refman/8.0/en/show-replica-status.html) · MySQL · 8.0.22からSHOW SLAVE STATUSの代わりにSHOW REPLICA STATUS、それより前のバージョンはSHOW SLAVE STATUS - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_replicationのwrite_lag・flush_lag・replay_lag:プライマリがWALを書いた後、レプリカが書き込み・ディスクへのフラッシュ・適用を通知するまでにかかった時間 - [Amazon CloudWatch metrics for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) · AWS · ReplicaLag:リードレプリカがソースDBより遅れている時間(秒) #### db-checkpoint · チェックポイント・ログフラッシュ · Checkpoint / log flush stalls DBがメモリ上の変更分を定期的にまとめてディスクに書き込む瞬間、クエリが遅くなります。 - なぜ → すると → 画面では:変更分がたまり、定期的にディスクへ書き出す → その瞬間ディスクの負荷が上がり、クエリが遅延 → 定期的に保存・ロードが遅くなる - 症状:入力遅延, カクつき / 要因:遅延 - 誰に:サーバー全体, 特定の機能だけ / いつ:一定の周期で - 主担当:インフラチーム・DBインフラ - インフラチームの対応:チェックポイントを細かく分けて平準化、トランザクションログ(redoログ、WAL)を十分な大きさに、高速なディスク。 - グラフでは:周期的なスパイク(DBクエリの遅延、ディスク書き込み量) - 確認箇所:PostgreSQLはlog_checkpoints(最近のバージョンはデフォルトで有効)のログにあるチェックポイントの時刻と書き込んだバッファ数、チェックポイントの回数(17以降はpg_stat_checkpointerのnum_timed・num_requested、16以前はpg_stat_bgwriterのcheckpoints_timed・checkpoints_req)、checkpoint_warningの警告を確認。MySQLはSHOW ENGINE INNODB STATUSのLOGセクションで、Log sequence numberとLast checkpoint atの差を確認。サーバーのディスク書き込み量・書き込み遅延も重ねて確認 - 該当する場合:クエリ遅延が跳ねた時刻がチェックポイントの時刻と重なり、そのときディスク書き込み量と書き込み遅延が跳ね上がる。PostgreSQLで要求によるチェックポイント(num_requested)が時間によるチェックポイント(num_timed)よりはるかに多ければ、WALが頻繁にmax_wal_sizeに達してチェックポイントが前倒しになっているとみなす - 該当しない場合:チェックポイントの時刻と無関係な周期で跳ねるなら、バックアップ・バッチ(dk-backup、db-batch) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:変更の記録を保持するトランザクションログ(MySQLのredoログ、PostgreSQLのWAL)を小さく設定しすぎると、ログが満杯になるたびに、DBが急いでチェックポイントをまとめて実行するため、書き込みスループットが一時的に大きく落ちます。 - 出典: - [WAL Configuration (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-configuration.html) · PostgreSQL · チェックポイントはデフォルトで5分ごと、またはWALが1GB(max_wal_size)に達するごとに実行され、ダーティページをすべて書き出すため高コスト。checkpoint_completion_targetで書き込みを分散し、I/Oの急増を避ける。チェックポイントの間隔がcheckpoint_warningより短いと、max_wal_sizeを増やすよう促す警告をログに出力 - [Configuring Buffer Pool Flushing](https://dev.mysql.com/doc/refman/8.4/en/innodb-buffer-pool-flushing.html) · MySQL · redoログが満杯になると急な(sharp)チェックポイントでスループットが一時的に落ちる、アダプティブフラッシュで平準化して書き出す - [Error Reporting and Logging (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-logging.html) · PostgreSQL · log_checkpoints:チェックポイントごとに書き込んだバッファ数とかかった時間をログに出力、デフォルト有効 - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_checkpointerのnum_timed(時間到達で実行したチェックポイント)・num_requested(要求されたチェックポイント) - [PostgreSQL 17 Release Notes](https://www.postgresql.org/docs/release/17.0/) · PostgreSQL · pg_stat_checkpointerを新設、チェックポイント関連のカラムをpg_stat_bgwriterから移動 - [The Cumulative Statistics System (PostgreSQL 16 Documentation)](https://www.postgresql.org/docs/16/monitoring-stats.html) · PostgreSQL · 16まではpg_stat_bgwriterのcheckpoints_timed・checkpoints_req - [InnoDB Standard Monitor and Lock Monitor Output](https://dev.mysql.com/doc/refman/8.4/en/innodb-standard-monitor.html) · MySQL · LOGセクション:現在のログシーケンス番号と最後のチェックポイントの位置 #### db-cold-cache · コールドキャッシュ(再起動直後) · Cold buffer pool after restart DBを再起動するとメモリ上のキャッシュが空になっているため、しばらくはすべての読み込みがディスクから行われます。 - なぜ → すると → 画面では:メンテナンスでDBを再起動 → よく使われていたデータがメモリになく、ディスクから読む → メンテ明けしばらくログイン・ロードが遅い - 症状:接続不可・無限ロード, 入力遅延 / 要因:遅延 - 誰に:サーバー全体 / いつ:接続直後・メンテ明け - 主担当:インフラチーム・DBインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:段階的なオープン(ログイン待機列を使い、ログインできる人数を少しずつ増やす)。 - インフラチームの対応:再起動後のキャッシュのウォームアップ(バッファプールの保存・復元設定を確認)、スナップショットから復元したDBはディスクも事前に読み込む。 - グラフでは:接続直後・メンテ明けに急増(ディスク読み込み、バッファキャッシュのヒット率) - 確認箇所:MySQLはInnodb_buffer_pool_reads(バッファプールになくディスクから読んだ回数)とInnodb_buffer_pool_read_requestsの比率、ウォームアップの進行状況Innodb_buffer_pool_load_statusを確認。PostgreSQLはpg_stat_databaseのblks_read・blks_hitを確認。DBサーバーのディスク読み込み数も合わせて確認 - 該当する場合:再起動直後にディスク読み込みが跳ね上がってヒット率が低く、時間とともに回復し、その間ログイン・ロードが遅い - 該当しない場合:ヒット率が平常どおりなのにメンテ明けに遅いなら、ログイン殺到・N+1(db-login-storm)かコネクションプール(db-pool) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:MySQLは停止時にバッファプールのページ一覧を保存し、起動時にバックグラウンドで読み込み直しますが、すべて埋まるまでには時間がかかります。クラウドでDBをスナップショット(ディスクのコピー)から復元した場合は、ディスク自体も初めて読むブロックごとに遅いため、さらに長引きます。 - 実際の事例:roblox-2021 - 出典: - [Saving and Restoring the Buffer Pool State](https://dev.mysql.com/doc/refman/8.4/en/innodb-preload-buffer-pool.html) · MySQL · 再起動後のウォームアップ時間を短くするため、停止時に最近使ったページの一覧(デフォルト25%)を保存し、起動時に読み込み直す、どちらもデフォルトで有効 - [pg_prewarm — preload relation data into buffer caches](https://www.postgresql.org/docs/current/pgprewarm.html) · PostgreSQL · 共有バッファの内容を定期的に記録しておき、再起動後に読み込み直す(autoprewarm) - [Initialize Amazon EBS volumes](https://docs.aws.amazon.com/ebs/latest/userguide/ebs-initialize.html) · AWS · スナップショットから作成したボリュームは、全ブロックを取得し終えるまで遅延が増え、性能が落ちる - [Server Status Variables](https://dev.mysql.com/doc/refman/8.4/en/server-status-variables.html) · MySQL · Innodb_buffer_pool_reads(バッファプールになくディスクから直接読んだ論理読み込みの数)、Innodb_buffer_pool_read_requests、Innodb_buffer_pool_load_status(ウォームアップの進行状況) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_databaseのblks_read(ディスクから読んだブロック数)・blks_hit(バッファキャッシュで見つかったブロック数) #### db-login-storm · ログイン殺到とN+1クエリ · Login storm, N+1 queries キャラクター1体を読み込むたびに数十回の個別クエリを発行していると、数万人の同時ログインは数百万件のクエリになります。 - なぜ → すると → 画面では:キャラクターのロード時にアイテム・スキル・クエストを個別にクエリ → メンテ明けの同時ログインでクエリが急増 → ログインの無限ロード、プレイ中のユーザーの保存まで滞る - 症状:接続不可・無限ロード, 入力遅延 / 要因:ストール, 遅延 - 誰に:サーバー全体 / いつ:接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:まとめて1回で取得、ログイン待機列、キャッシュ、ORMの遅延ロードが生むクエリ数の点検。 - インフラチームの対応:呼び出し回数の多いクエリのランキングを出して共有、メンテ明けのログイン時間帯のクエリ数・コネクション数を監視。 - グラフでは:接続直後・メンテ明けに急増(DBの秒間クエリ数、ログイン数) - 確認箇所:メンテ明けのログイン数とDBの秒間クエリ数(MySQLはQuestionsの増加量)を重ねて、ログイン1件あたりのクエリ数を計算。呼び出し回数が上位のクエリは、MySQLはevents_statements_summary_by_digestのCOUNT_STAR、PostgreSQLはpg_stat_statementsのcallsで抽出 - 該当する場合:ログイン1件あたりのクエリが数十件あり、上位のクエリがキャラクターID 1つで検索する同じ形の短いクエリばかり。アップデート後にログインあたりのクエリ数が増えたなら、そのアップデートが発端 - 該当しない場合:ログインあたりのクエリ数は少ないのにクエリ1つ1つが遅いなら、コールドキャッシュ(db-cold-cache)かインデックス(db-no-index) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:ORM(DBへのクエリを代わりに組み立ててくれるライブラリ)の遅延ロード(lazy loading)が、開発者も気づかないうちにこうしたクエリを生みます。開発サーバーではキャラクターが数体しかないので目立たず、本番の同時ログインで初めて表面化します。 - 出典: - [Efficient Querying](https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying) · .NET · ORMの遅延ロードは項目ごとにクエリをもう1回ずつ送るN+1問題を生み、性能を大きく落とす、一度にまとめて読み込む方式(eager loading)を推奨 - [pg_stat_statements — track statistics of SQL planning and execution](https://www.postgresql.org/docs/current/pgstatstatements.html) · PostgreSQL · ステートメントごとの実行回数(calls)と合計実行時間を集計し、呼び出しの多いクエリのランキングを出す - [Performance Schema Statement Digests and Sampling](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-statement-digests.html) · MySQL · events_statements_summary_by_digestで同じ形のクエリをまとめ、回数・時間を集計 - [Statement Summary Tables](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-statement-summary-tables.html) · MySQL · サマリーテーブルのCOUNT_STAR(実行回数)・SUM_TIMER_WAIT(合計時間) - [Server Status Variables](https://dev.mysql.com/doc/refman/8.4/en/server-status-variables.html) · MySQL · Questions:クライアントが送信したステートメントの数 #### db-batch · 大規模なバッチ処理 · Batch jobs during service ランキング集計、郵便の一括送信、古いデータの整理をサービス中に実行すると、ロックとディスクを占有します。 - なぜ → すると → 画面では:サービス時間中に大量の処理を実行 → 広い範囲のロック、ディスク・CPUの占有 → 特定の時間帯に取引・保存が失敗、ロード遅延 - 症状:入力遅延, 不発・ロールバック / 要因:遅延, ストール - 誰に:特定の機能だけ, サーバー全体 / いつ:一定の周期で - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:細かく分けて少しずつ、集計はレプリカで。 - インフラチームの対応:集計用レプリカの提供、バッチは空いている時間帯に予約、ロックエスカレーション・ギャップロック待ちの監視。 - グラフでは:周期的なスパイク(DBクエリの遅延、ロック待ち) - 確認箇所:ラグが出た時刻に動いていた長いクエリを探す。MySQLはslow query log、PostgreSQLはpg_stat_activityのquery_start・queryを確認し、同じ時刻のロック待ちのメトリクスとバッチのスケジュール(cron、DBのイベントスケジューラー)を照合。SQL Serverはlock_escalation拡張イベントでロックエスカレーションを記録 - 該当する場合:毎回同じ時刻に大量のUPDATE・DELETE・集計クエリが動き、その間ロック待ちとディスク利用率がそろって上がる - 該当しない場合:その時刻に長いクエリがなければ、チェックポイント(db-checkpoint)かサーバーのバックアップ(dk-backup) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:SQL Serverは、1つのステートメントが約5,000個を超える行ロックを取ると、テーブルロックに切り替えます(ロックエスカレーション)。その瞬間、同じテーブルを使うすべての要求が止まります。MySQLもデフォルト設定では、範囲条件で更新すると行と行の間の隙間までロックし(ギャップロック)、新しい行の挿入を止めます。 - 出典: - [Transaction Locking and Row Versioning Guide](https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-transaction-locking-and-row-versioning-guide) · Microsoft SQL Server · 1つのステートメントが1つのテーブル(またはインデックス)で5,000個以上のロックを取るとロックエスカレーション、lock_escalation拡張イベントで記録 - [InnoDB Locking](https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html) · MySQL · InnoDBのデフォルトの分離レベルREPEATABLE READでは、検索・スキャンにnext-keyロックを使うため、ギャップロックがその隙間への新しい行の挿入を止める - [The Slow Query Log](https://dev.mysql.com/doc/refman/8.4/en/slow-query-log.html) · MySQL · long_query_timeを超えたクエリを、実行時間(Query_time)・ロック時間(Lock_time)・読んだ行数とともに記録 - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_activity:セッションごとの現在実行中のクエリ(query)と開始時刻(query_start) #### db-failover · DBのフェイルオーバー · Database failover プライマリが落ちてスタンバイDBに切り替わる間は書き込みができず、レプリケーションが間に合わなかった最後のデータは失われることがあります。 - なぜ → すると → 画面では:プライマリの障害でスタンバイDBが昇格 → 切り替え中は数秒〜数分書き込み不可、非同期レプリケーションならレプリカに届いていないデータが失われる可能性 → 一時的にすべての保存が失敗、アイテム・経験値のロールバック - 症状:不発・ロールバック, フリーズ, 切断, 接続不可・無限ロード / 要因:ストール, パケットロス - 誰に:サーバー全体 / いつ:ときどきランダムに - 主担当:インフラチーム・DBインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:再試行可能な保存、切れた接続をすぐに捨てて新しいアドレスへ接続し直す設定(コネクションプール・DNSキャッシュ)、フェイルオーバー訓練で再接続を確認。 - インフラチームの対応:同期・準同期レプリケーション(書き込み遅延とのトレードオフ)、フェイルオーバー訓練、切り替え時間・レプリケーション遅延の監視。 - 数値の目安:マネージドDBの自動フェイルオーバーは通常数十秒〜2分。非同期レプリケーションなら、レプリケーション遅延の分(1秒未満〜数秒)だけ直近の保存が失われることがあります。 - グラフでは:接続が一斉に切れる(DB接続数、書き込みエラー数) - 確認箇所:DB側のフェイルオーバーの記録(RDSはイベントRDS-EVENT-0013がフェイルオーバー開始・RDS-EVENT-0049がフェイルオーバー完了、自前で運用するDBは昇格ログ)と、ゲームサーバーのDB接続数・接続エラー数を1つのグラフに並べる。非同期レプリケーションなら、障害直前のレプリケーション遅延(RDSのReplicaLag、PostgreSQLのpg_stat_replicationのreplay_lag)も確認 - 該当する場合:保存の失敗が1つの区間に集中し、その区間がフェイルオーバーの開始から完了までと重なる。ロールバックされた分が障害直前のレプリケーション遅延とほぼ同じ。切り替え完了後もエラーが続くゲームサーバーは、古いアドレスで確立した接続を使い続けている - 該当しない場合:フェイルオーバーの記録がない時刻の接続断は、ネットワークかDBの過負荷側 - 確認手段:インフラのツールで確認(ゲームコード不要) - 実際の事例:riot-euw-2021 - 出典: - [Failing over a Multi-AZ DB instance for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.Failover.html) · AWS · Multi-AZのフェイルオーバーは通常60〜120秒、フェイルオーバー後は接続を張り直す必要があり、JVMのDNSキャッシュTTLは60秒以下を推奨 - [High availability for Amazon Aurora](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.AuroraHighAvailability.html) · AWS · 障害中は読み書きが失敗し、通常60秒以内(多くは30秒以内)に復旧 - [Semisynchronous Replication](https://dev.mysql.com/doc/refman/8.4/en/replication-semisync.html) · MySQL · 非同期レプリケーションでは、プライマリが落ちるとコミット済みのトランザクションがレプリカにないことがある、準同期はレプリカ1台の受信確認を待つことでこれを減らす代わりに遅延が増える - [Log-Shipping Standby Servers (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/warm-standby.html) · PostgreSQL · ログシッピングは非同期のため、プライマリが落ちるとまだ送っていないトランザクションを失う、ストリーミングレプリケーションの遅延は通常1秒未満 - [Amazon RDS event categories and event messages](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.Messages.html) · AWS · RDS-EVENT-0013:Multi-AZフェイルオーバー開始、RDS-EVENT-0049:Multi-AZフェイルオーバー完了 - [Amazon CloudWatch metrics for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) · AWS · ReplicaLag:リードレプリカがソースDBより遅れている時間(秒) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_replicationのreplay_lag:プライマリがWALを書いた後、レプリカが適用を通知するまでにかかった時間 #### db-save-interval · 長い保存間隔による進行状況の消失 · Periodic save window 負荷を減らすために数分に1回しか保存しないと、その間にサーバーが落ちた場合に進行状況が失われます。 - なぜ → すると → 画面では:キャラクターの状態を数分ごとに1回保存 → その間にサーバーのクラッシュ・障害が発生 → 再接続すると数分前の状態(ロールバック) - 症状:不発・ロールバック / 要因:パケットロス - 誰に:サーバー全体, 特定の場所・チャンネル / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:重要なイベント(取引、レアアイテムの獲得)は即時保存、変更ログの記録。 - インフラチームの対応:保存間隔を短くしたときに増える書き込みに耐えられるだけの、DBのIOPS・CPUの余裕を確認。 - グラフでは:接続が一斉に切れる(接続数、ロールバックの報告数) - 確認箇所:クラッシュ・障害の時刻と、ロールバックを報告したキャラクターの最終保存時刻(ゲームサーバーの保存ログやDBの更新日時カラム)を並べる - 該当する場合:巻き戻った時点がクラッシュ直前の最終保存時刻と一致し、失われた時間が保存間隔より短い - 該当しない場合:ゲームサーバーのログには保存完了と残っているのに巻き戻ったなら、DBのフェイルオーバーによるデータ消失(db-failover)か、レプリカから読んだ古い値(db-replica-lag) - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Asynchronous Commit (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-async-commit.html) · PostgreSQL · 記録をまとめて遅れて書き出すとスループットが上がる代わりに、障害時に直近のトランザクションが失われることがある(同じトレードオフ) - [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/) · Redis · RDBスナップショットを数分ごとに作る場合、異常終了時には直近数分のデータを失う覚悟が必要 #### db-cache-stampede · キャッシュスタンピード · Cache stampede / thundering herd 人気データのキャッシュが同時に期限切れになると、数千件の要求が一斉にDBへ殺到します。 - なぜ → すると → 画面では:Redisなどに保存した人気データが同時に期限切れ → 同じデータを作り直そうとする要求が一斉にDBへ殺到 → DBの過負荷で複数の機能が次々に遅くなったり止まったりする - 症状:入力遅延, フリーズ, 接続不可・無限ロード / 要因:ストール, 遅延 - 誰に:サーバー全体 / いつ:一定の周期で, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:期限切れの時刻をランダムに分散、1つの要求だけが更新し残りは古い値を使う。 - インフラチームの対応:Redisの再起動・障害時にキャッシュが丸ごと空にならないよう、レプリカ・自動フェイルオーバーを構成、キャッシュが空になっても耐えられるDBの余裕を確認。 - グラフでは:周期的なスパイク(キャッシュのヒット率、DBの秒間クエリ数) - 確認箇所:Redis INFOのkeyspace_hits・keyspace_misses(ヒット率)、expired_keys、再起動の有無(uptime_in_seconds)をDBの秒間クエリ数と重ねて確認し、その瞬間にDBで同じクエリがいくつ同時に動いているか(MySQLはSHOW PROCESSLIST、PostgreSQLはpg_stat_activity)を数える - 該当する場合:キャッシュミスが一瞬で急増した時刻にDBのクエリ数も跳ね上がり、同時に動いているクエリの大半が同じデータを読む同じクエリ。人気キーの期限切れの周期やRedisの再起動時刻と重なる - 該当しない場合:キャッシュミスは平常どおりなのにDBのクエリだけが増えるなら、ログイン殺到(db-login-storm)かバッチ(db-batch) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:Redisが再起動したり、障害でキャッシュが丸ごと空になったりしても、同じことが起きます。キャッシュを当てにしてDBを小さく見積もった構成ほど危険です。 - 出典: - [Scaling Memcache at Facebook (NSDI '13)](https://www.usenix.org/conference/nsdi13/technical-sessions/presentation/nishtala) · USENIX · よく使うキーが無効化されると大量の読み取りがDBに殺到するthundering herd、リース(1つのクライアントだけが更新)・古い値の返却で防ぐ、キャッシュが空のクラスターは別途ウォームアップ - [Optimal Probabilistic Cache Stampede Prevention](https://www.vldb.org/pvldb/vol8/p886-vattani.pdf) · VLDB Endowment · 人気の項目が期限切れになると複数の要求が同時に再生成するキャッシュスタンピード、期限切れの前に確率的に先回りして更新して防ぐ - [High availability with Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/) · Redis · プライマリが落ちるとレプリカを昇格させる自動フェイルオーバー - [INFO](https://redis.io/docs/latest/commands/info/) · Redis · keyspace_hits・keyspace_misses(キー参照の成功・失敗数)、expired_keys(期限切れになったキーの数)、uptime_in_seconds(起動からの経過時間) - [SHOW PROCESSLIST Statement](https://dev.mysql.com/doc/refman/8.4/en/show-processlist.html) · MySQL · セッションごとの実行中のステートメント(Info)と、現在の状態にとどまっている時間(Time、秒) - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_activity:セッションごとの現在実行中のクエリ(query) #### db-long-tx · 長時間開いたままのトランザクション · Long-running transaction / MVCC purge lag 1つのトランザクションが長く開いたままだと、ロックを持ち続けるうえ、DBが古いバージョンのデータを整理(purge)できないため、全体が徐々に遅くなります。 - なぜ → すると → 画面では:トランザクションを開いたまま別サーバーの応答を待つ、またはサービス中にプライマリで長い集計クエリを実行 → 取ったロックが解放されず、整理すべき古いバージョンのデータがたまり続ける → その行を使う機能がタイムアウト、数時間かけて保存・読み込みが全体的に遅くなる - 症状:入力遅延, 不発・ロールバック / 要因:遅延, ストール - 誰に:特定の機能だけ, サーバー全体 / いつ:長時間稼働するほど, ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:トランザクション内でネットワーク呼び出し・ユーザー入力を待たない、集計クエリはレプリカで。 - インフラチームの対応:長時間開いたままのトランザクションのアラートと強制終了、集計用レプリカの提供、UNDOログ・デッドタプルの増加の監視。 - グラフでは:徐々に上昇(UNDOログの長さ(History list length)、デッドタプル数) - 確認箇所:MySQLはINFORMATION_SCHEMA.INNODB_TRXのtrx_startedで最も古いトランザクションを探し、SHOW ENGINE INNODB STATUSのTRANSACTIONSセクションに出るHistory list length(まだ整理できていないUNDOログの量)を確認。PostgreSQLはpg_stat_activityのxact_startと、stateがidle in transactionのセッション、pg_stat_user_tablesのn_dead_tupを確認 - 該当する場合:数分〜数時間経ったトランザクションがあり、その間History list lengthやn_dead_tupが上がり続け、そのトランザクションを終えた後に整理(purge・VACUUM)が走って減る - 該当しない場合:古いトランザクションがないのに全体的に遅いなら、チェックポイント(db-checkpoint)かディスク側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:DBは、読む側が更新前の状態を見られるように、古いバージョンを残しておきます(MVCC)。最も古いトランザクションが終わるまでこの記録は消せないため、1つのトランザクションが数時間開いたままだと、MySQLではUNDOログが、PostgreSQLではVACUUMが整理できないデッドタプル(dead tuple、不要になった古い行)がたまります。SQL Serverではトランザクションログが縮まず、ディスクを埋めてしまうこともあります。 - 出典: - [InnoDB Multi-Versioning](https://dev.mysql.com/doc/refman/8.4/en/innodb-multi-versioning.html) · MySQL · 古いバージョンを参照しうるトランザクションが残っていると、update UNDOログを破棄できずロールバックセグメントが大きくなる、読み取りだけのトランザクションもこまめにコミットするよう推奨 - [Routine Vacuuming (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/routine-vacuuming.html) · PostgreSQL · 古い行バージョンは他のトランザクションから見える間は削除できず、長時間開いたままのトランザクションは終了させるかセッションを切断する必要がある - [Client Connection Defaults (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-client.html) · PostgreSQL · idle_in_transaction_session_timeout:トランザクションを開いたままアイドル状態になっているセッションを切断し、ロックを長く保持できないようにする - [Troubleshoot a full transaction log (SQL Server Error 9002)](https://learn.microsoft.com/en-us/sql/relational-databases/logs/troubleshoot-a-full-transaction-log-sql-server-error-9002) · Microsoft SQL Server · 長時間実行中のアクティブなトランザクションが、トランザクションログの切り捨てを妨げる - [The INFORMATION_SCHEMA INNODB_TRX Table](https://dev.mysql.com/doc/refman/8.4/en/information-schema-innodb-trx-table.html) · MySQL · TRX_STARTED:トランザクションの開始時刻 - [Purge Configuration](https://dev.mysql.com/doc/refman/8.4/en/innodb-purge-configuration.html) · MySQL · コミット済みトランザクションのUNDOログの一覧(history list)をpurgeが整理、滞っている量はSHOW ENGINE INNODB STATUSのTRANSACTIONSセクションのHistory list lengthで表示 - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · pg_stat_activityのxact_start(トランザクションの開始時刻)とstate(idle in transaction)、pg_stat_user_tablesのn_dead_tup(デッドタプル数の推定値) #### db-redis-block · Redisの遅いコマンド · Redis blocking commands (single-threaded) Redisはコマンドを1つずつ順番に処理するため、遅いコマンドが1つあると、その後ろのすべての要求が止まります。 - なぜ → すると → 画面では:サービス中にKEYSで全件検索、要素が数百万個あるランキング・リストを丸ごと読んだり削除したりする → そのコマンドが終わるまで、他のすべての要求が待たされる(数十ms〜数秒) → セッション・ランキング・キャッシュを使う機能が一斉に一瞬止まる、ログイン遅延 - 症状:フリーズ, 入力遅延, 接続不可・無限ロード / 要因:ストール, 遅延 - 誰に:サーバー全体, 特定の機能だけ / いつ:ときどきランダムに, 一定の周期で - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:KEYSの代わりにSCAN、大きなキーの分割、削除はUNLINK(バックグラウンド削除)、同じ秒に集中する期限切れ時刻の分散。 - インフラチームの対応:遅いコマンドの記録(SLOWLOG)の監視、KEYSのような危険なコマンドは本番サーバーで禁止、大きなキーの定期点検、THPを無効にしてfork用のメモリの余裕を確保、RDB・AOFの保存はレプリカで。 - 数値の目安:通常のコマンドは1ms未満。要素数百万個を一度に扱うと、数百msから数秒かかることもあります。 - グラフでは:不定期なスパイク(Redisの応答遅延、遅いコマンドの数) - 確認箇所:SLOWLOG GETでslowlog-log-slower-thanを超えたコマンドを確認し、CONFIG SET latency-monitor-thresholdでレイテンシモニター(デフォルト無効)を有効にしてから、LATENCY LATEST・LATENCY DOCTORでfork・expire-cycleのようなイベントごとの遅延を確認。INFOのlatest_fork_usecとredis-cli --bigkeysで、fork時間と大きなキーも確認 - 該当する場合:止まった時刻のSLOWLOGにKEYSや大きなキーを丸ごと扱うコマンドが残っている、またはLATENCYに同じ時刻のfork・expire-cycleイベントが数十ms以上で記録されている - 該当しない場合:SLOWLOG・LATENCYが空なのにゲームサーバー側でだけ遅いなら、ネットワークかゲームサーバー内部の待ち(SLOWLOGはコマンドの実行時間だけを測り、クライアントとのやり取りの時間は含まない) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:保存ファイル(RDBスナップショット)を作ったり、AOFをリライトしたりするためにプロセスを複製(fork)する瞬間にも止まります。最近のサーバーではメモリ1GBあたり約10msなので、30GBなら300msほどです。ヒュージページ(THP)を有効にしていると、fork後の書き込みのたびにヒュージページを丸ごとコピーする(copy-on-write)ため、停止時間とメモリ使用量が大きく増えます。そのため通常はTHPを無効にし、メモリに十分な余裕を持たせます。同じ秒に期限切れになるキーが非常に多いときも、Redisが削除処理のために一瞬止まります。 - 出典: - [Diagnosing latency issues](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency/) · Redis · 要求を1つのスレッドが順番に処理するため、遅いコマンドが後ろをすべて止める、KEYSの代わりにSCAN、forkは物理サーバー・最新VMの実測で1GBあたり約9〜13ms、THPはfork後のコピーで遅延・メモリが急増、同じ秒に大量の期限切れが起きると停止 - [KEYS](https://redis.io/docs/latest/commands/keys/) · Redis · 本番環境では細心の注意を払って使うこと、大きなDBでは性能を大きく損なうおそれがある(エントリーモデルのノートPCでキー100万個に40ms) - [UNLINK](https://redis.io/docs/latest/commands/unlink/) · Redis · キーを即座に切り離し、メモリの回収は別スレッドで行う非同期削除 - [SLOWLOG](https://redis.io/docs/latest/commands/slowlog/) · Redis · slowlog-log-slower-thanを超えたコマンドを記録するスローログ、実行時間にはクライアントとのI/Oは含まれない - [Redis latency monitoring](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor/) · Redis · latency-monitor-thresholdのデフォルトは0(無効)、LATENCY LATEST・LATENCY DOCTOR、fork・expire-cycleのようなイベントごとの遅延を記録 - [INFO](https://redis.io/docs/latest/commands/info/) · Redis · latest_fork_usec:直近のforkにかかった時間(マイクロ秒) - [Redis CLI](https://redis.io/docs/latest/develop/tools/cli/) · Redis · --bigkeys:キー空間を走査して大きなキーを探す #### db-plan-flip · 実行計画の変化によるクエリ遅延 · Query plan regression (stats, parameter sniffing) コードは変わっていないのに、DBが同じクエリの処理方法(実行計画)を変えると、昨日2msだったクエリが今日は数百msになります。 - なぜ → すると → 画面では:統計情報の自動更新、DBの再起動、データ分布の変化で、DBが実行計画を立て直す → インデックスを使わない計画が選ばれ、同じクエリが数十〜数百倍遅くなり、コネクションがふさがる → デプロイもなかったのに特定の機能のロードが急に遅くなり、他の要求まで待たされる - 症状:入力遅延, 接続不可・無限ロード / 要因:遅延, ストール - 誰に:特定の機能だけ, サーバー全体 / いつ:ときどきランダムに - 主担当:インフラチーム・DBインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:値によって結果の件数が大きく変わるクエリは分けて使うか、実行計画のヒントを検討、インデックスが確実に効くクエリの設計。 - インフラチームの対応:スロークエリと実行計画の記録の監視、良い計画の固定(SQL Serverのクエリストアなど)、統計情報の更新時刻の管理。 - グラフでは:ある時点から階段状に上昇(クエリごとの平均実行時間) - 確認箇所:同じ形のクエリの平均時間を定期的に集めて推移を確認。MySQLはevents_statements_summary_by_digestのAVG_TIMER_WAIT、PostgreSQLはpg_stat_statementsのmean_exec_time(12以前はmean_time)。遅くなる前後の実行計画は、EXPLAINやPostgreSQLのauto_explainで、SQL Serverはクエリストアの「機能低下したクエリ」(Regressed Queries)画面で比較 - 該当する場合:デプロイのなかった時刻に1つのクエリの平均時間が階段状に数十倍に上がり、その時点が統計情報の更新・DBの再起動と重なり、実行計画が変わっている - 該当しない場合:実行計画は変わっていないのに遅くなったなら、データの増加、ロック待ち(db-hot-row)、ディスク側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:SQL Serverは、最初に渡された値に合わせて立てた計画を再利用します(パラメータスニッフィング)。アイテムが数個しかない新規キャラクターで立てた計画が、アイテムを数万個持つ古いキャラクターに使われると大きく遅くなり、その逆もよくあります。再起動で計画が消えると正常に戻り、また悪化することもあります。 - 出典: - [Query Processing Architecture Guide](https://learn.microsoft.com/en-us/sql/relational-databases/query-processing-architecture-guide) · Microsoft SQL Server · パラメータスニッフィング:コンパイル・再コンパイル時に渡されたパラメータ値に合わせて実行計画を立てる - [Parameter Sensitive Plan Optimization](https://learn.microsoft.com/en-us/sql/relational-databases/performance/parameter-sensitive-plan-optimization) · Microsoft SQL Server · データ分布が偏っていると、キャッシュされた1つの計画がすべてのパラメータ値には合わない - [Monitor performance by using the Query Store](https://learn.microsoft.com/en-us/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store) · Microsoft SQL Server · 統計情報・スキーマ・インデックスの変化で計画が変わり、プランキャッシュは最新の計画しか保持しない、クエリストアのプラン強制で良い計画を固定、「機能低下したクエリ」(Regressed Queries)画面で遅くなったクエリと計画を比較 - [Statement Summary Tables](https://dev.mysql.com/doc/refman/8.4/en/performance-schema-statement-summary-tables.html) · MySQL · events_statements_summary_by_digest:同じ形のクエリごとのCOUNT_STAR・AVG_TIMER_WAIT(平均時間) - [pg_stat_statements — track statistics of SQL planning and execution](https://www.postgresql.org/docs/current/pgstatstatements.html) · PostgreSQL · ステートメントごとのcalls、total_exec_time、mean_exec_time(平均実行時間) - [pg_stat_statements (PostgreSQL 12 Documentation)](https://www.postgresql.org/docs/12/pgstatstatements.html) · PostgreSQL · 12まではカラム名がtotal_time・mean_time - [auto_explain — log execution plans of slow queries](https://www.postgresql.org/docs/current/auto-explain.html) · PostgreSQL · auto_explain.log_min_durationより長くかかったクエリの実行計画をログに出力 #### db-ddl-lock · サービス中のスキーマ変更(DDL)によるロック · Schema change lock (DDL / metadata lock) サービス中にテーブルへカラムやインデックスを追加すると、一瞬だけ必要なロック1つのために、そのテーブルを使うすべての要求が待たされることがあります。 - なぜ → すると → 画面では:ホットフィックスでサービス中のテーブルにカラム・インデックスを追加 → スキーマ変更が先に開いていた長いトランザクションを待ち、後から来るすべての要求はそのスキーマ変更を待つ → そのテーブルを使う機能(インベントリ、郵便など)が丸ごと止まり、タイムアウト - 症状:入力遅延, 不発・ロールバック, 接続不可・無限ロード / 要因:ストール - 誰に:特定の機能だけ, サーバー全体 / いつ:ときどきランダムに, 接続直後・メンテ明け - 主担当:インフラチーム・DBインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:スキーマ変更を含むホットフィックスはDBインフラと日程を調整、新しいカラムがなくても動くコードを先にデプロイ。 - インフラチームの対応:ロック待ちの上限を短く設定し、失敗したら再試行、長いトランザクションがないときに実行、オンラインスキーマ変更ツールの使用、大きなテーブルはメンテナンス時間に。 - グラフでは:ある時点から階段状に上昇(ロック待ちのセッション数、そのテーブルのクエリ遅延) - 確認箇所:MySQLはSHOW PROCESSLISTでStateがWaiting for table metadata lockのセッションを数え、sys.schema_table_lock_waitsでブロックしているセッション(blocking_pid)を探す。PostgreSQLはpg_locksでgrantedがfalseの要求とAccessExclusiveLockを確認し、pg_blocking_pids()でブロックしているセッションを探す - 該当する場合:スキーマ変更を開始した時刻から、そのテーブルを使うすべてのクエリがロック待ちで積み上がり、先頭に終わっていないトランザクションかスキーマ変更のステートメントがある - 該当しない場合:待ちが特定の行にだけ集中し、同じテーブルの他の行は問題なく処理されているなら、ホットスポット(db-hot-row) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:MySQLはスキーマを変更するときにメタデータロックを、PostgreSQLは最も強いテーブルロックを短時間取ります。変更自体は一瞬で終わっても、先に終わっていないトランザクションが1つあると、その後ろにすべての要求が並んで待ちます。 - 出典: - [Online DDL Performance and Concurrency](https://dev.mysql.com/doc/refman/8.4/en/innodb-online-ddl-performance.html) · MySQL · オンラインDDLでも完了時に排他メタデータロックが一瞬必要、長いトランザクションがあれば待ち、待っているロック要求が後続のすべてのトランザクションを止める - [Server System Variables](https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html) · MySQL · lock_wait_timeout:メタデータロック待ちの上限、デフォルト値は31,536,000秒(1年) - [ALTER TABLE (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/sql-altertable.html) · PostgreSQL · 特に記載のないALTER TABLEは、最も強いACCESS EXCLUSIVEロックを取る - [Client Connection Defaults (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/runtime-config-client.html) · PostgreSQL · lock_timeout:ロックをこの時間を超えて待つとステートメントを中断 - [General Thread States](https://dev.mysql.com/doc/refman/8.4/en/general-thread-states.html) · MySQL · Waiting for table metadata lock:メタデータロックを待っているスレッドの状態 - [The schema_table_lock_waits and x$schema_table_lock_waits Views](https://dev.mysql.com/doc/refman/8.4/en/sys-schema-table-lock-waits.html) · MySQL · メタデータロックを待っているセッション(waiting_query)とブロックしているセッション(blocking_pid) - [pg_locks (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/view-pg-locks.html) · PostgreSQL · grantedがfalseならロック待ち、modeにAccessExclusiveLockなどのロックの種類 - [System Information Functions and Operators (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/functions-info.html) · PostgreSQL · pg_blocking_pids():指定したセッションのロック取得をブロックしているセッションの一覧 ### L13 サーバー構成と運用(原因13件) #### in-gateway · ゲートウェイ・プロキシ経由 · Gateway / proxy hop クライアントとゲームサーバーの間に中継サーバーを置くと、経由するたびに処理時間が加わり、そのサーバーが単一障害点になります。 - なぜ → すると → 画面では:クライアント ↔ ゲートウェイ ↔ ゲームサーバーの構成 → 中継サーバーでの処理・待ちが加わり、過負荷になると全員に影響 → 全体のPingが上昇、ゲートウェイ障害時はそこを経由するユーザー全員が切断 - 症状:入力遅延, 切断 / 要因:遅延, ストール - 誰に:サーバー全体 / いつ:人が集中したとき, 常に - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:ゲートウェイを複数台に増やせる構成、ゲートウェイが落ちても別のゲートウェイにつなぎ直せばキャラクターがそのまま引き継がれる仕組み(セッション再接続)。クライアント:ゲートウェイとの接続が切れたら自動で再接続。 - インフラチームの対応:ゲートウェイの水平スケーリング(台数追加)、ゲートウェイごとのCPU・接続数・処理遅延の監視。 - 数値の目安:同じデータセンター内なので、普段は1回の経由で1ms未満です。ゲートウェイが過負荷になると数十〜数百msに延びます。 - グラフでは:人数・負荷に連動して上昇(ゲートウェイの処理遅延、ゲートウェイのCPU・接続数) - 確認箇所:ゲートウェイのCPU・接続数とゲートウェイのソケットのRecv-Q(ss・netstat)、ゲートウェイを通る前と後の遅延の差。サービスメッシュを経由するHTTP・gRPC呼び出しなら、Istioの標準メトリクスistio_request_duration_millisecondsを送信側(reporter=source)と受信側(reporter=destination)に分けて比較 - 該当する場合:ゲームサーバーの処理時間は変わらないのにゲートウェイ区間の遅延だけが増え、その時刻にゲートウェイのCPUが飽和しているかRecv-Qがたまっている - 該当しない場合:ゲートウェイを経由しない経路(直接接続、別のゲートウェイ)も同じように遅ければ、回線かゲームサーバー側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:サービスメッシュ(Istioなど)を使うと、サーバーごとに横に付くサイドカープロキシ(Envoy)も1段加わります。サービス間のリクエストは送信側のサイドカーと受信側のサイドカーを順に経由し、プロキシにログ・メトリクス収集などの機能を足すほど、処理時間と待ち時間が増えます。 - 実際の事例:riot-edge-2020 - 出典: - [The Unique Architecture behind Amazon Games’ Seamless MMO New World](https://aws.amazon.com/blogs/gametech/the-unique-architecture-behind-amazon-games-seamless-mmo-new-world/) · AWS · New Worldでは、クライアントがパブリックアドレスを持つ入口サーバー(REP)4台のうち1台に接続し、その後ろのシミュレーションサーバー(ハブ)と通信 - [Designs, Lessons and Advice from Building Large Distributed Systems](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · LADIS 2009の基調講演(Jeff Dean)。同じデータセンター内の往復は約0.5ms - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · 過負荷でキューが長くなると、待ち時間が処理時間の数倍に延びる(処理100ms、キューがスレッド数の10倍なら1.1秒) - [Performance and Scalability](https://istio.io/latest/docs/ops/deployment/performance-and-scalability/) · Istio · サイドカーモードでは、リクエストは送信側と受信側のサイドカープロキシを順に経由、機能を足すほどプロキシ内の処理経路が長くなり、テレメトリ収集が次のリクエストの待ち時間を延ばす - [What is Envoy](https://www.envoyproxy.io/docs/envoy/latest/intro/what_is_envoy) · Envoy · Envoyはすべてのアプリケーションサーバーの横で別に動くプロセスで、アプリはlocalhostのEnvoyを経由して通信 - [Istio Standard Metrics](https://istio.io/latest/docs/reference/config/metrics/) · Istio · istio_request_duration_milliseconds(HTTP・gRPCリクエストの処理時間の分布)、reporterラベルで送信側(source)・受信側(destination)のプロキシを区別 - [netstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/netstat.8.html) · net-tools · Recv-Q:接続済みソケットで、ユーザープログラムがまだ読み取っていないバイト数 #### in-zone-transfer · ゾーン移動(サーバー間の引き継ぎ) · Zone / server handoff 別のエリアやダンジョンに入るとき、キャラクター情報を別のサーバーへ引き渡す過程で遅延や失敗が起きます。 - なぜ → すると → 画面では:ダンジョン入場や大陸移動で担当サーバーが変わる → 保存 → 転送 → 読み込み、移動先サーバーが混雑しているか空いているダンジョンインスタンスがなければ待機 → 長いロード、入場失敗、移動中の切断 - 症状:接続不可・無限ロード, フリーズ, 切断, 引き戻し / 要因:遅延, ストール - 誰に:自分だけ, 特定の場所・チャンネル / いつ:移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:引き継ぐデータの削減、移動先サーバーの事前予約、失敗時は元の場所へ戻す。 - インフラチームの対応:ダンジョン・ゾーンサーバーの空きインスタンスの余裕を監視、ピーク前に台数を確保。 - グラフでは:人数・負荷に連動して上昇(ゾーン移動の所要時間・失敗数) - 確認箇所:サーバーが記録する引き継ぎの段階別所要時間(保存、転送、読み込み)と失敗理由、移動先サーバーの人数と空きインスタンス数 - 該当する場合:長いロード・入場失敗の報告があった時刻に引き継ぎ時間が延びるか失敗が集中し、移動先サーバーが混雑しているか空きインスタンスが尽きている - 該当しない場合:引き継ぎはすぐ終わったのに到着後に止まるなら、密集エリア進入時のスポーン集中か、クライアントのロード側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:ロードなしでつながったシームレスワールドでも、サーバーの境界を越えるときに担当サーバーが変わります。境界付近で一瞬止まったり、引き戻しが起きたりすることがあります。 - 出典: - [The Unique Architecture behind Amazon Games’ Seamless MMO New World](https://aws.amazon.com/blogs/gametech/the-unique-architecture-behind-amazon-games-seamless-mmo-new-world/) · AWS · ロードのないワールドをグリッドに分けて複数のサーバー(ハブ)が担当し、プレイヤーが移動すると状態をハブからハブへ引き渡す、セッション型モードは共有プールから空いているサーバーを割り当てて使う #### in-cascade · カスケード障害 · Cascading failure 1つのサービスが遅くなると、それを呼び出すサーバーが応答待ちで塞がり、関係のない機能まで止まります。 - なぜ → すると → 画面では:DB・認証など1つのサービスが遅くなる → 呼び出し側サーバーのスレッドと接続が応答待ちで塞がり、失敗したリクエストの再試行が負荷を上乗せ → 関係なさそうな機能まで、すべて遅くなるか止まる - 症状:フリーズ, 入力遅延, 接続不可・無限ロード / 要因:ストール - 誰に:サーバー全体 / いつ:人が集中したとき, ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:すべての呼び出しにタイムアウト、サーキットブレーカー、機能ごとの隔離(バルクヘッド)、再試行は間隔を広げながら回数を制限、ヘルスチェックの応答は重い処理と分離。 - インフラチームの対応:ロードバランサーのヘルスチェックが一時的に遅いだけのサーバーをすぐ外さないよう、失敗回数・間隔に余裕を持たせる、一度に外れるサーバー数の制限。 - グラフでは:上限で頭打ち(サービスごとの応答時間・エラー率、スレッド・コネクションの使用数) - 確認箇所:サービスごとの応答時間・エラー率・再試行回数を時間軸をそろえて1画面に並べ、最初に遅くなった箇所を探す。ロードバランサーの後ろなら、ターゲットの応答時間(AWS ALBではTargetResponseTime)、ターゲットの5xx数(HTTPCode_Target_5XX_Count)、異常として外れたターゲット数(UnHealthyHostCount) - 該当する場合:1つのサービスの遅延が先に上がり、続いてそのサービスを呼び出す側のスレッド・コネクション使用数が上限に張り付き、エラーがほかのサービスへ広がり、再試行回数と外れたターゲット数も一緒に増える - 該当しない場合:複数のサービスが同じ瞬間に一斉に遅くなったなら、共用リソース(DB、ネットワーク、ホスト)の障害から確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:ヘルスチェック(生きているかを確認する検査)も連鎖を広げます。忙しいサーバーがチェックへの応答に遅れると、ロードバランサーが正常なサーバーを外してしまい、そのトラフィックが残りのサーバーに集中して次のサーバーも遅くなります。 - 実際の事例:riot-edge-2020, riot-euw-2021, roblox-2021, aws-2021, aws-2025 - 出典: - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · 過負荷のサーバーがヘルスチェックに失敗して外れると残りのサーバーに負荷が集中し、再試行が負荷を増やす、再試行回数の制限・ランダム化した指数バックオフ・デッドラインを推奨 - [Circuit Breaker Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) · Microsoft Azure · タイムアウトまで詰まったリクエストがスレッド・DB接続を占有し、関係のない機能まで失敗させる、一定時間内に失敗がたまると呼び出しを即座に拒否 - [Timeouts, retries, and backoff with jitter](https://d1.awsstatic.com/builderslibrary/pdfs/timeouts-retries-and-backoff-with-jitter.pdf) · AWS · Amazon Builders' Library。5段の呼び出しで各層が3回ずつ再試行するとDB負荷は243倍、再試行は1か所だけで行い、トークンバケットで制限 - [CloudWatch metrics for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-cloudwatch-metrics.html) · AWS · TargetResponseTime(リクエストがロードバランサーを出てからターゲットが応答を開始するまでの時間)、HTTPCode_Target_5XX_Count(ターゲットが返した5xxの数)、UnHealthyHostCount(異常なターゲットの数) #### in-subservice · 補助サーバーの障害 · Auxiliary service outage チャット・パーティ・オークションのように、ゲームサーバーとは別に動くサーバーで障害が起きると、その機能だけが動かなくなります。 - なぜ → すると → 画面では:機能専用サーバーが遅くなるか落ちる → その機能のリクエストだけ応答なし → チャットができない、パーティ招待に反応なし、取引所が無限ロード(戦闘は正常) - 症状:不発・ロールバック, 接続不可・無限ロード / 要因:ストール, パケットロス - 誰に:特定の機能だけ / いつ:ときどきランダムに, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:失敗してもゲームは続けられる設計、機能ごとの状態表示、複数の機能を中央サーバー1台に集めない。 - インフラチームの対応:補助サーバーごとのヘルスチェック・アラート、冗長化と自動再起動。 - グラフでは:接続が一斉に切れる(機能ごとのリクエスト成功率、補助サーバーの接続数・ヘルスチェック) - 確認箇所:チャット・パーティ・オークションなど補助サーバーごとのヘルスチェック・プロセス状態と接続数、機能ごとのリクエスト成功率・応答時間。ロードバランサーの後ろならターゲットグループのUnHealthyHostCount - 該当する場合:報告された機能を担当するサーバーだけがヘルスチェック失敗や接続数の急落を示し、ゲームサーバーのティックと戦闘は正常 - 該当しない場合:複数の機能が一斉に止まったなら、それらの機能をまとめて中継する中央サーバーかカスケード障害側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:パーティ・ギルド・ささやき(ウィスパー)・サーバー間移動を1台の中央サーバー(ワールドサーバー・マネージャーサーバー)がすべて中継する構成では、そのサーバー1台が遅くなるだけで複数の機能が一斉に止まります。 - 出典: - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · Microsoft Azure · コンポーネントをプールごとに隔離すれば、1つが失敗しても残りは動き続け、障害が広がらない - [REL05-BP01 Implement graceful degradation to transform applicable hard dependencies into soft dependencies](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_mitigate_interaction_failure_graceful_degradation.html) · AWS · AWS Well-Architected。依存先が障害中でも、中核機能は少し古いデータや代替データで動き続けるように設計 - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · UnHealthyHostCount:ヘルスチェックで異常と判定されたターゲットの数 #### in-deploy · デプロイ・再起動 · Deploy / rolling restart アップデートのためにサーバーを再起動するとき、接続を移さずに止めると、そのサーバーにいた人は切断され、終了直前の保存と再接続が一気に集中します。 - なぜ → すると → 画面では:ホットフィックスのデプロイでサーバーを順番に再起動 → 接続を別のサーバーに移さずに終了、そのサーバーにいた全ユーザーの保存がDBに集中 → 告知なしの切断、再接続の殺到 - 症状:切断, 接続不可・無限ロード, 入力遅延 / 要因:ストール - 誰に:サーバー全体, 特定の場所・チャンネル / いつ:ときどきランダムに, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:ドレイン機能(新規接続だけを止め、既存のユーザーが抜けるまで待つ)、キャラクターを別のサーバーへ移す、終了前の保存を分散して行う、再起動後はキャッシュのロード・JITウォームアップを終えてから準備完了を通知、ホットリロードは別スレッドで先に読み込んでおき、ティックの合間に一度に差し替え。 - インフラチームの対応:デプロイツールが1台ずつドレインを待ってから再起動、再起動したサーバーは準備完了(ウォームアップ完了)を確認してからトラフィックを受ける、デプロイ時刻の告知。 - 数値の目安:サーバー1台に5,000人いれば、終了直前の数秒間に5,000件の保存がDBに集中します。 - グラフでは:接続が一斉に切れる(サーバーごとの接続数、DB書き込み数) - 確認箇所:デプロイツールの作業記録(サーバーごとの再起動時刻)を、接続数・切断数・DB書き込み・ログインリクエストのグラフに縦線(アノテーション)として重ねて確認 - 該当する場合:サーバーごとの接続数が再起動時刻に1台ずつ順番に急落し、その直前にDB書き込みが、直後にログインリクエストが跳ね上がる - 該当しない場合:切断の時刻がデプロイ・再起動の記録と重ならなければ、サーバークラッシュかネットワーク機器側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:再起動した直後の数分間も遅くなります。キャッシュが空なのでDBへの問い合わせが集中し、Java・C#のサーバーは実行しながらコードを最適化する過程(JITウォームアップ)が終わる前なので、同じ処理により時間がかかります。サーバーを止めずにスクリプトやデータテーブルを読み直す方式(ホットリロード)でも、読み込み中はティックが止まるため、短い停止が起きます。 - 出典: - [Site Reliability Engineering, Chapter 20: Load Balancing in the Datacenter](https://sre.google/sre-book/load-balancing-datacenter/) · Google · SIGTERMを受けたサーバーはlame duck状態になり、新しいリクエストを別のサーバーへ回して処理中のリクエストだけを終える、再起動直後の数分はJIT最適化前でリソースを多く使うため、ウォームアップ後にトラフィックを受けさせる - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · Readiness Probeで、接続の確立・ファイルのロード・キャッシュのウォームアップが終わるまでトラフィックを送らない - [Edit target group attributes for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/edit-target-group-attributes.html) · AWS · ターゲットの登録を解除すると、新しい接続は送らず既存の接続はドレイン(デフォルト300秒) #### in-autoscale · オートスケーリングの遅れ · Autoscaling lag 人が集中するとサーバーを自動で増やしますが、準備に数分かかり、その間は既存のサーバーが過負荷になります。 - なぜ → すると → 画面では:イベント開始で接続が急増 → 新しいサーバーが起動して準備が整うまで数分 → イベント開始直後の数分間、スローモーション・接続不可 - 症状:スローモーション, 接続不可・無限ロード / 要因:ストール - 誰に:サーバー全体 / いつ:人が集中したとき, 接続直後・メンテ明け - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:チャンネル分散(すでに混雑しているチャンネルにいる人は新しいサーバーに移せない)、新しいサーバーの起動・データロード時間の短縮。 - インフラチームの対応:イベント前の事前スケールアウト、ウォームアップ済みの予備サーバー、縮小時は残っている人が抜けてから停止。 - 数値の目安:負荷の検知に1〜数分(メトリクスを数分間平均して見るため)、新しいサーバーを起動してゲームデータを読み込み、キャッシュを埋めるのにさらに数分。 - グラフでは:接続直後・メンテ明けに急増(インスタンス数、CPU使用率、接続待ち) - 確認箇所:オートスケーリングのアクティビティ履歴(スケールアウトを決めた時刻、新しいインスタンスがサービスに入った時刻)をCPU使用率・接続数のグラフに重ねて確認。AWSではAuto Scalingグループのメトリクス(有効にしないと表示されない)GroupDesiredCapacity(目標台数)・GroupPendingInstances(準備中)・GroupInServiceInstances(サービス中) - 該当する場合:接続が急増してから数分間、目標台数と準備中のインスタンスだけが増えて既存サーバーのCPUが上限に張り付き、サービス中のインスタンスが増えた時刻から解消する - 該当しない場合:新しいインスタンスが入った後も遅いなら、サーバー台数以外の原因(DBなどの共用リソース、カスケード障害)側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:オートスケーリングは主に、ログイン・ゲートウェイ・ダンジョンのように、新しいサーバーで新しい人を受け入れればよい所に使います。縮小するときにも問題が起きます。人が減った明け方にサーバーを減らすとき、残っている人が抜けるのを待たずに止めると、その人たちは切断されます。 - 実際の事例:aws-2021, aws-2025 - 出典: - [Target tracking scaling policies for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html) · AWS · EC2の基本メトリクスは5分間隔(詳細モニタリングを有効にすると1分)なので、素早く反応させるには1分以下の間隔のメトリクスを推奨 - [Amazon CloudWatch metrics for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-metrics.html) · AWS · グループメトリクスは有効にすると1分単位で発行される、GroupDesiredCapacity(維持しようとする台数)・GroupPendingInstances(まだサービス前のインスタンス数)・GroupInServiceInstances(サービス中のインスタンス数) - [Scheduled scaling for Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-scheduled-scaling.html) · AWS · 予測できる負荷の変化に合わせ、決めた時刻に容量を前もって増減 - [Decrease latency for applications with long boot times using warm pools](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-warm-pools.html) · AWS · 起動に時間がかかるアプリは、事前に初期化しておいたインスタンスのプール(ウォームプール)でスケールアウトの遅れを減らす #### in-monitoring · ログ・監視の過負荷 · Logging / monitoring overhead 障害が起きるとログが急増し、ログを同期で送るサーバーはログのせいでさらに遅くなります。 - なぜ → すると → 画面では:エラー発生に伴い、ログ・メトリクスの送信量が急増 → ログコレクターが詰まり、同期送信するサーバーは待たされる → 障害時のカクつき・フリーズがログのせいでさらに悪化 - 症状:カクつき, フリーズ / 要因:ストール - 誰に:サーバー全体 / いつ:人が集中したとき, ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ - ゲーム開発チームの対応:非同期送信、サンプリング、バッファがあふれたら破棄、同じエラーログはまとめて送る。 - インフラチームの対応:ログコレクターの容量を障害時の急増量を基準に確保、コレクターの滞留アラート。 - グラフでは:不定期なスパイク(ログ送信量、ログコレクターのキュー) - 確認箇所:サーバーの毎秒のログ行数・バイト数と、ログ収集エージェントのキュー・破棄数をティック時間と並べて確認。止まっているスレッドがあれば、bcc offcputime -pでログの書き込み・送信で待っているかを確認 - 該当する場合:ティックが跳ねた時刻にログ量が普段の数十倍に跳ね上がり、ゲームスレッドの待ち時間がログ書き込み・送信のコールスタックに集中している - 該当しない場合:ログ量が普段と同じか、ゲームスレッドがログ側で待っていないなら、ログの急増は障害の結果にすぎないため、最初にエラーを出した原因を別に探す - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Logging in C#](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging) · Microsoft · .NETのログメソッドは同期なので、保存先が遅い場合は直接書かずに、まず高速な保存先に書いて後で移すよう推奨 - [Asynchronous loggers](https://logging.apache.org/log4j/2.x/manual/async.html) · Apache Software Foundation · 非同期ロギングは短い急増をキューで吸収するが、出力が遅い状態が続くとキューが埋まり、最も遅い出力の速度まで落ちるか、ポリシーに従ってログを破棄(Discard) - [Demonstrations of offcputime, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/offcputime_example.txt) · IO Visor · スレッドが止まってCPUを離れていた時間(off-CPU)をコールスタックごとに集計、-pでプロセスを指定 #### in-clock-skew · サーバー間の時刻のずれ · Clock skew between servers サーバーごとに時計が少しずつずれていると、クールタイム・バフ・イベント開始の判定がサーバーごとに食い違います。 - なぜ → すると → 画面では:時刻同期が止まったサーバーの時計が、ほかのサーバーと数百ms〜数秒ずれる → バフの終了時刻のような絶対時刻をサーバー間で受け渡すと判定が食い違う → 移動したらバフが消える、クールタイムがまた最初から始まる - 症状:不発・ロールバック / 要因:遅延 - 誰に:自分だけ / いつ:移動中・マップ切り替え時 - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:サーバー間では絶対時刻の代わりに残り時間で受け渡す。 - インフラチームの対応:時刻同期(NTP・chrony)の監視、サーバー間の時刻のずれのアラート。 - 数値の目安:時刻同期(NTP・chrony)が正常なら、同じデータセンターのサーバー同士では通常数ms以内です。同期が止まったり、仮想サーバーが長く停止してから再開したりすると、数百ms〜数秒に広がります。 - グラフでは:徐々に上昇(サーバーごとの時刻オフセット) - 確認箇所:サーバーごとにchronyc trackingのSystem time(システム時計とNTP時計の差)・Last offsetとRef time(時刻ソースの測定値を最後に反映した時刻)を集めて比較 - 該当する場合:問題のサーバーのオフセットがほかのサーバーより数百ms以上ずれているか、Ref timeがずっと前で止まっていて、判定の食い違いがそのサーバーを行き来する移動でだけ起きる - 該当しない場合:すべてのサーバーのオフセットが数ms以内なら、ゲーム側の時間計算か、クライアントの時刻同期の誤差側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:1台のサーバーの時計が一気に前後へ跳ぶ現象は、サーバーOS層の「システム時刻のジャンプ(NTPステップ)」で扱います。 - 出典: - [RFC 5905: Network Time Protocol Version 4: Protocol and Algorithms Specification](https://www.rfc-editor.org/rfc/rfc5905) · IETF · 高速なLANのNTPクライアントは通常数百µs以内に合う - [chrony – Frequently Asked Questions](https://chrony-project.org/faq.html) · chrony · 一般的なコンピューターの時計のドリフトは100ppm未満だが、仮想マシンではもっと大きくなりうる、一時停止から再開した仮想マシンは時刻がずれ、ステップ補正が必要になることがある - [clock_gettime(2) — Linux manual page](https://man7.org/linux/man-pages/man2/clock_gettime.2.html) · Linux man-pages · CLOCK_REALTIMEは手動変更・NTP補正で不連続に跳ぶことがあり、CLOCK_MONOTONICはそうしたジャンプの影響を受けない - [chronyc(1)](https://chrony-project.org/doc/4.6/chronyc.html) · chrony · chronyc trackingのSystem time(NTP時計とシステム時計の差)、Last offset(最後の補正時に推定したオフセット)、Ref time(時刻ソースの最後の測定値を反映した時刻) #### in-bots · 大量のマクロ・ボット · Bots and macros ボットは人よりはるかに頻繁にリクエストを送り、サーバーのスループットを食いつぶします。 - なぜ → すると → 画面では:狩り・移動・取引を休まず繰り返すボットが大量に接続 → サーバーの処理量とDB負荷が増加 → 特定の狩場やサーバー全体が重くなる(スローモーション・入力遅延) - 症状:スローモーション, 入力遅延 / 要因:ストール - 誰に:サーバー全体, 特定の場所・チャンネル / いつ:常に, 夜のピーク時間帯 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:ボット検知、アカウント・キャラクターごとのリクエスト頻度の制限。 - インフラチームの対応:IPごとの接続・リクエスト頻度の制限(ネットカフェやモバイル回線は複数人が1つのIPを使うので余裕を持たせる)、ファイアウォール・WAFでボットのアドレス帯を遮断。 - グラフでは:一部だけ高い(アカウント・IPごとの毎秒リクエスト数) - 確認箇所:ゲームサーバーのログで、アカウント・キャラクターごとの毎秒リクエスト数の分布と上位リストを確認。コード側のメトリクスがなければ、ファイアウォール・WAFのIPごとのリクエスト数 - 該当する場合:少数のアカウント・IPが人間には不可能な頻度で休みなくリクエストしており、それらを制限するとサーバー負荷が目に見えて下がる - 該当しない場合:リクエストがアカウント間で均等に分散していれば、通常の人数増加側(ティックバジェット超過、オートスケーリングの遅れ) - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Using rate-based rule statements in AWS WAF](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based.html) · AWS · IPなどのキーごとにリクエスト数を数え、決めた時間枠内で多すぎればレート制限 - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · 複数の契約者が1つのIPを共有していると、IP単位の遮断・制限がほかのユーザーまで巻き込む #### in-external · 外部サービスへの依存 · External dependencies (auth, billing, platform) プラットフォームのログイン、決済、本人確認などの外部サービスが遅くなったり止まったりすると、その段階で先に進めなくなります。 - なぜ → すると → 画面では:外部の認証・決済サービスの障害や遅延 → その段階で応答待ち → ログイン不可、決済失敗。すでにプレイ中の人は問題なし - 症状:接続不可・無限ロード, 不発・ロールバック / 要因:ストール - 誰に:サーバー全体, 特定の機能だけ / いつ:接続直後・メンテ明け, 特定の操作をしたとき - 主担当:外部・外部 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:外部呼び出しにタイムアウトとわかりやすい案内、認証結果のキャッシュ、決済の再試行・補償手続き。 - 外部の対応:認証・決済・プラットフォームの事業者に障害の確認と復旧を依頼、ユーザーに外部サービスの障害であることを案内。 - グラフでは:ある時点から階段状に上昇(外部呼び出しの応答時間・エラー率、ログイン成功数) - 確認箇所:プラットフォームのログイン・決済・本人確認など外部呼び出しごとの応答時間・エラー率・タイムアウト数と、事業者のステータスページ - 該当する場合:ログイン・決済の失敗が集中した時刻から、特定の外部呼び出しのエラー・タイムアウトだけが一段上がったまま推移し、事業者のステータスページに同じ時刻の障害が載っている - 該当しない場合:外部呼び出しは正常なのにログインできないなら、ログインサーバー自体(スレッドプールの枯渇、DB)かOSの接続待ちキュー(backlog)側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 実際の事例:fastly-2021, aws-2021, aws-2025 - 出典: - [Timeouts, retries, and backoff with jitter](https://d1.awsstatic.com/builderslibrary/pdfs/timeouts-retries-and-backoff-with-jitter.pdf) · AWS · Amazon Builders' Library。応答を待つ間はスレッド・接続などのリソースを占有するのでタイムアウトを設け、副作用のあるAPIは冪等性が保証される場合だけ再試行 - [Circuit Breaker Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) · Microsoft Azure · 失敗する可能性が高い呼び出しは、タイムアウトまで待たずにすぐ拒否して応答時間を守る - [REL05-BP01 Implement graceful degradation to transform applicable hard dependencies into soft dependencies](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_mitigate_interaction_failure_graceful_degradation.html) · AWS · AWS Well-Architected。依存サービスが障害中でも、少し古いデータを使ってでも中核機能を維持(認証結果キャッシュの根拠) #### in-region-match · マッチメイキング・リージョン割り当ての誤り · Wrong region assignment (matchmaking / GeoDNS) 近いリージョンがあるのに遠いリージョンのサーバーに割り当てられると、回線に問題がなくても、そのユーザーだけPingが常に高くなります。 - なぜ → すると → 画面では:GeoIPデータの誤り、VPN、パーティメンバーの平均Pingでパーティ全体を割り当て、人数が足りないときに遠いリージョンまで広げるルール、DNSリゾルバーの位置を基準にした割り当て → 近いリージョンがあるのに、海の向こうのリージョンのサーバーに接続 → 複数のリージョンにサーバーを置いたゲームで、自分だけ(または自分のパーティだけ)Pingが常に高く、入力遅延・引き戻し・スキル不発 - 症状:入力遅延, 引き戻し, 不発・ロールバック / 要因:遅延 - 誰に:自分だけ, 特定の地域・ISP / いつ:常に, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発, インフラチーム・ネットワークインフラ, 外部・外部 - ゲーム開発チームの対応:サーバー:GeoIPの代わりにクライアントが測定したリージョン別のPingで割り当て、遠いリージョンへ広げるルールにPingの上限を設ける、パーティは平均に加えて最も高いメンバーのPingも考慮、割り当てたリージョンとその時のPingをログに記録。クライアント:リージョン別のPingをUDPで測ってマッチングリクエストに添えて送る、接続中のリージョンとPingを画面に表示、リージョンを自分で選べる選択肢。 - インフラチームの対応:DNSでリージョンを選ぶなら、権威DNSがEDNS Client Subnetに対応しているかを確認(ユーザーが使うリゾルバーが送らなければリゾルバーの位置で割り当てられる)、GeoIPデータベースの定期更新、リージョン別サーバーの接続ログにGeoIPの国・ASNを付けて、遠いリージョンへ行っている国・通信事業者を特定。 - 外部の対応:ユーザーにVPN・ラグ軽減ツールを切って再接続してみるよう案内、会社や海外のDNSを使うユーザーに通信事業者のDNSへ切り替えてみるよう案内、GeoIP事業者に誤った位置の訂正を依頼。 - 数値の目安:ソウルのユーザーが東京リージョンの代わりに米国西海岸リージョンに割り当てられると、Pingは約30msから約130msに増えます。GeoIPは国単位なら約99.8%当たりますが、都市単位では米国でも50km以内に収まる割合が約66%です。VPNを使うと、ユーザーの位置の代わりにVPNサーバーの位置が返ります。 - グラフでは:一部だけ高い(ユーザーごとのRTT(Ping)、割り当てられたリージョンの分布) - 確認箇所:リージョン別サーバーの接続記録(ロードバランサーのアクセスログ、VPCフローログ)に残ったクライアントIPにGeoIPの国・ASNを付け、国・通信事業者ごとにどのリージョンに接続したかを集計。ユーザー1人なら、そのユーザーが実際に接続したリージョンと、近いリージョンまでのPing(ユーザーが測るか、そのリージョンのサーバーからユーザーのIPへmtr)を比較 - 該当する場合:RTTが高いユーザー・国が近いリージョンを使わずに遠いリージョンに接続しており、近いリージョンまで測ったPingは低い - 該当しない場合:近いリージョンに正しく割り当てられているのにPingが高ければ、迂回ルーティングか、そのユーザーの回線・Wi-Fi側 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:DNSでリージョンを選ぶ方式(位置情報・レイテンシベースのDNS)は、ユーザー本人の代わりに、ユーザーが使うDNSリゾルバーのアドレスを見て位置を推測します。ユーザーのアドレスの一部を伝えるEDNS Client Subnetにリゾルバーが対応していなければ、会社のDNSや遠くにあるDNSを使うユーザーは、リゾルバーのある場所を基準に割り当てられます。マッチングシステムも、パーティをメンバーのPingの平均で判断したり、待ち時間が長くなるとPingの基準を広げて遠いリージョンに割り当てたりします。AWS GameLift Serversも、パーティのPingのデフォルト基準は平均で、Pingの上限を50msから100ms、200msへと広げていく設定を例に挙げています。VPNを有効にしたユーザーは、中継サーバーを経由して増えた遅延(「VPN・ラグ軽減ツール経由」)と遠いリージョンへの割り当てが重なることがあるため、VPNを切って再接続したときに割り当てられるリージョンが変わるかどうかで切り分けます。近いリージョンがそもそもなく、遠いリージョンに接続する場合は「伝搬遅延(物理的な距離)」で扱います。 - 出典: - [RFC 7871: Client Subnet in DNS Queries](https://www.rfc-editor.org/rfc/rfc7871) · IETF · 位置によって異なる応答を返すDNSは、問い合わせを送ったリゾルバーのアドレスで位置を推測し、ユーザーから遠く離れた集中型のリゾルバーを使うと不適切な応答になる。EDNS Client Subnet(オプション機能)でユーザーのアドレスの一部を伝える - [How Amazon Route 53 uses EDNS0 to estimate the location of a user](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-edns0.html) · AWS · リゾルバーがedns-client-subnetに対応していなければ、リゾルバーのアドレスでユーザーの位置を推測し、リゾルバーの位置を基準に応答(位置情報・レイテンシベースのルーティングに共通) - [Geolocation accuracy](https://support.maxmind.com/knowledge-base/articles/maxmind-geolocation-accuracy) · MaxMind · 国単位で約99.8%、米国の都市単位(50km以内)で約66%、VPNを使うとエンドユーザーの代わりにVPNサーバーの位置、モバイル回線のIPは広い地域で使われるため細かい位置はわからない、データベースは継続的な更新が必要、訂正依頼が可能 - [FlexMatch rule types](https://docs.aws.amazon.com/gameliftservers/latest/flexmatchguide/match-rules-reference-ruletype.html) · AWS · レイテンシルール(maxLatency)は場所ごとのプレイヤーの遅延を見て、パーティにはデフォルトでメンバーの平均(partyAggregation avg)を使う、キューはレイテンシルールに合わないリージョンにも配置しうる - [Create a player latency policy](https://docs.aws.amazon.com/gameliftservers/latest/developerguide/queues-design-latency.html) · AWS · 全プレイヤーの平均遅延が最も低い場所に配置するが、遅延が極端なプレイヤーも配置される、Pingの上限を50msから100ms、200msへと広げていくポリシーの例 - [Amazon GameLift Servers UDP ping beacons](https://docs.aws.amazon.com/gameliftservers/latest/developerguide/reference-udp-ping-beacons.html) · AWS · ゲームクライアントがホスティング拠点ごとのUDPエンドポイントで遅延を測り、配置・マッチングに使う、ICMPによるPingより実際のゲームトラフィックに近い - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · ソウル(Korea Central)を起点とした実測の往復中央値:東京(Japan East)29ms、米国西海岸124〜136ms - [Flow log records](https://docs.aws.amazon.com/vpc/latest/userguide/flow-log-records.html) · AWS · VPCフローログレコードのsrcaddr:受信トラフィックなら送信元のIPアドレス #### in-cert · TLS証明書の期限切れ・設定ミス · TLS certificate expiry / misconfiguration ログイン・API・アップデートサーバーの証明書が期限切れになったり中間証明書が抜けていたりすると、その瞬間から新たに接続するクライアントのTLS接続が失敗します。 - なぜ → すると → 画面では:証明書の有効期限が切れている、サーバーが中間証明書を含めずに送っている、またはユーザー端末の日付・時刻がずれている → クライアントが証明書の検証に失敗し、TLS接続を切断 → ログイン・アップデートの段階で接続不可・無限ロード、ショップなどHTTPSの機能だけ失敗。すでに接続していた人はたいてい問題なし - 症状:接続不可・無限ロード, 不発・ロールバック / 要因:ストール - 誰に:サーバー全体, 特定の機能だけ, 自分だけ / いつ:接続直後・メンテ明け, 特定の操作をしたとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:証明書エラーをほかの接続失敗と区別できるエラーコードで記録して案内、日付のずれが原因なら端末の日付・時刻を自動設定にするよう案内、証明書ピンニング(pinning)を使うなら予備の鍵も一緒に組み込み、証明書の差し替え日程をインフラチームと合わせる。 - インフラチームの対応:ネットワーク:ロードバランサー・CDNでTLSを終端する場合、マネージド証明書の自動更新の状態と残り日数のアラート(ACMではDaysToExpiry)、検証用DNSレコードの維持。サーバー機器・OS:サーバーでTLSを終端する場合、更新の自動化と更新後の設定の再読み込み、中間証明書まで含めたチェーンで設定、ログイン・API・アップデートの各アドレスについて残りの有効期間を外部から定期的に検査してアラート。 - 数値の目安:Let’s Encryptの証明書は有効期間が90日で、60日ごとの更新が推奨されています。AWS Certificate Managerは、DNSで検証した証明書を期限の45日前に確認して自動更新します。自動更新が気づかれないまま失敗すると、ちょうど期限の時刻に新規接続が一斉にできなくなります。 - グラフでは:接続が一斉に切れる(ログイン成功数、TLSハンドシェイクのエラー数) - 確認箇所:openssl s_client -connect HOST:443 -showcertsでサーバーが実際に送る証明書の一覧を確認し、各証明書の有効期限(notAfter)をopenssl x509 -noout -enddateで確認。ロードバランサーでTLSを終端しているなら、TLSネゴシエーションのエラー数(AWS ALB・NLBではClientTLSNegotiationErrorCount)とログイン成功数 - 該当する場合:有効期限が過ぎているか、サーバーが送った一覧に中間証明書が含まれておらず、エラーが増え始めた時刻が期限の時刻か証明書を差し替えた時刻と重なる - 該当しない場合:証明書の一覧と有効期限が正常なのに一部のユーザーだけ失敗するなら、そのユーザー端末の日付・時刻か、古いOSのルート証明書リストを確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:中間証明書が抜けた設定は、PCのブラウザで開くと問題なく見えることがあります。ブラウザはほかのサイトで受け取った中間証明書を覚えていて欠けた部分を補いますが、Androidアプリのようにその記憶を持たないクライアントは失敗します。有効期間も短くなりつつあります。Let’s Encryptはデフォルトの有効期間を2027年に64日、2028年に45日へ短縮する予定で、60日ごとに更新するよう固定した設定では、64日の証明書だと余裕が4日しかなく、45日の証明書では期限を過ぎてしまいます。AWS Certificate Managerも、インポートした証明書は自動更新せず、検証用のDNSレコードを消すと更新に失敗します。ログインできなくなる様子は「DNSの障害・遅延」と似ていますが、証明書の問題はサーバーのアドレスを解決した後のTLSハンドシェイクで失敗し、始まった時刻が期限の時刻や証明書を差し替えた時刻と重なります。 - 出典: - [RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile](https://www.rfc-editor.org/rfc/rfc5280) · IETF · 証明書の有効期間はnotBeforeからnotAfterまで、パス検証ではチェーンの証明書ごとに現在時刻が有効期間内かを確認(検証する側の時計がずれていると失敗) - [FAQ](https://letsencrypt.org/docs/faq/) · Let's Encrypt · デフォルトの証明書有効期間は90日、60日ごとの更新を推奨 - [Decreasing Certificate Lifetimes to 45 Days](https://letsencrypt.org/2025/12/02/from-90-to-45) · Let's Encrypt · デフォルトの有効期間を2027年2月に64日、2028年2月に45日へ短縮、60日の固定間隔での更新では足りなくなるため、有効期間の約3分の2の時点での更新を推奨 - [Renewal for domains validated by DNS](https://docs.aws.amazon.com/acm/latest/userguide/dns-renewal-validation.html) · AWS · 期限の45日前に、AWSサービスで使用中かと検証用CNAMEレコードがあるかを確認して自動更新、検証できなければ期限の30・15・7・3・1日前に通知 - [Managed certificate renewal in AWS Certificate Manager](https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html) · AWS · インポートした証明書とすでに期限切れの証明書は、自動更新の対象外 - [Supported CloudWatch metrics](https://docs.aws.amazon.com/acm/latest/userguide/cloudwatch-metrics.html) · AWS · DaysToExpiry:証明書の期限までの残り日数、期限まで1日2回発行 - [Security with network protocols](https://developer.android.com/privacy-and-security/security-ssl) · Android (Google) · サーバーが中間証明書を含めずに送ると、AndroidアプリはSSLHandshakeExceptionで失敗するが、PCのブラウザは保持している中間証明書で補うためエラーにならないことがある、openssl s_clientでサーバーが送るチェーンを確認 - [Network security configuration](https://developer.android.com/privacy-and-security/security-config) · Android (Google) · 証明書ピンニングを使うなら、鍵の差し替え・CAの変更に備えて予備の鍵も組み込む必要があり、そうしないとアプリを更新するまで接続できなくなる - [openssl-s_client](https://docs.openssl.org/3.0/man1/openssl-s_client/) · OpenSSL · -showcerts:サーバーが送った証明書の一覧を送られた順のまま表示(検証済みのチェーンではない) - [openssl-x509](https://docs.openssl.org/3.0/man1/openssl-x509/) · OpenSSL · -enddate:証明書の有効期限(notAfter)を出力、-checkend:指定した秒数以内に期限が切れるかを検査 - [CloudWatch metrics for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-cloudwatch-metrics.html) · AWS · ClientTLSNegotiationErrorCount:クライアントがサーバー証明書の検証に失敗して接続を切った場合など、TLSセッションを確立できなかった接続の数 - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · ClientTLSNegotiationErrorCount:クライアントとTLSリスナーの間のネゴシエーションに失敗したTLSハンドシェイクの数 #### in-login-queue · ログイン待機列の上限・再接続猶予の不足 · Login queue cap / no reconnect grace リリース直後やメンテ明けに接続が集中すると、ログイン待機列が上限に達して新たな待機を拒否し、待っていたユーザーは一瞬切れた間に順番を失って最後尾に戻されます。 - なぜ → すると → 画面では:ログインサーバーが一度に受け付けられる人数より接続しようとする人が多いため待機列を設け、長くなりすぎるとサーバーを守るために新たな待機を拒否 → 待機列が長いほど待ち時間が延び、その間にWi-Fi・モバイル回線が一瞬途切れただけで順番を失う → 接続不可・無限ロード、待機中にエラーが出てゲームが終了、また最後尾から待ち直し - 症状:接続不可・無限ロード, 切断 / 要因:ストール - 誰に:サーバー全体, 自分だけ / いつ:接続直後・メンテ明け, 夜のピーク時間帯 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発, インフラチーム・サーバーインフラ - ゲーム開発チームの対応:サーバー:待機列の上限をログインサーバーが実際に処理できる量に合わせる、待機中に切れたユーザーの順番を一定時間確保(再接続猶予)、順番と予想待ち時間の表示、待機列の長さ・拒否数・待機中の切断数をメトリクスとして記録。クライアント:待機中に切れてもゲームを終了せず同じ順番へ自動で再接続、再試行間隔は指数バックオフとジッターで分散。 - インフラチームの対応:サーバー機器・OS:リリース前の負荷試験でログイン・ロビーサーバーの処理上限を測定、リリース時は予備機をすぐ追加できるよう準備、待機列のメトリクスを接続試行数と同じグラフで確認。 - 数値の目安:2021年のFINAL FANTASY XIV拡張パッケージのリリース時には、論理データセンターごとに待機人数が17,000人を超えると新たな待機を拒否していました(Error 2002)。待機中に接続が切れた場合はロビーサーバーが数十秒〜1分待ち、その間に再接続すれば待機列の途中から再開できるようにしていました。 - グラフでは:上限で頭打ち(ログイン待機列の長さ、上限到達による拒否数、待機中の切断数) - 確認箇所:ログイン・ロビーサーバーが記録する待機列の長さ、平均待ち時間、上限到達による拒否数、待機中の切断数を、接続試行数と同じグラフに並べて確認 - 該当する場合:リリース直後・メンテ明けに待機列の長さが上限で頭打ちになっている間に拒否数が増え、待機中の切断がWi-Fi・モバイル回線のユーザーに集中 - 該当しない場合:待機列は短いのにログインが遅いなら、DB(db-login-storm)かOSの接続待ちキュー(so-backlog)側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:ログインが集中してDBが遅くなるケースは「ログイン殺到とN+1クエリ」、OSの接続待ちキューがあふれるケースは「接続待ちキュー(backlog)のあふれ」で扱います。この項目は、ゲームが意図的に設けるログイン待機列の設計の問題です。待機列の上限はログインサーバーを守る安全装置なので、なくせません。あふれたリクエストを早めに拒否してこそ、処理できるリクエストを処理し続けられるからです。その代わり、拒否や切断がユーザーに与える不利益を減らすことが肝心です。待機列が長くなるほど、Wi-Fi・モバイル回線のように回線が不安定なユーザーにエラーが集中します。 - 実際の事例:ffxiv-2021 - 出典: - [Response to Congestion (as of Dec. 11)](https://na.finalfantasyxiv.com/lodestone/news/detail/6a94b30182b6d963994fdc0b789264ac9f24986f) · Square Enix · 論理データセンターごとに待機人数が17,000人を超えると、ログインサーバーがダウンしないよう新たな待機を拒否(Error 2002)、待機中に切れるとロビーサーバーが数十秒〜1分待ち、その間に再接続すれば待機列の途中から、過ぎれば最後尾へ - [Using load shedding to avoid overload](https://aws.amazon.com/builders-library/using-load-shedding-to-avoid-overload/) · Amazon Builders' Library · あふれたリクエストを早めに拒否し、処理できるリクエストを処理し続けるロードシェディング ### 同期設計(原因16件) #### sy-request-response · サーバー応答後にだけ演出(リクエスト・レスポンス方式) · Request-response (no client-side feedback) ボタンを押しても、サーバーの応答が来るまでアニメーションも音も出ません。Pingがそのまま反応速度になります。 - なぜ → すると → 画面では:スキル・移動・アイテム拾いを、サーバーの確認が来てから再生 → 押した瞬間から、往復時間 + ティック待ちの間まったく反応なし → Pingが150msなら、すべての行動が0.2秒ずつ遅れてもっさりする - 症状:入力遅延 / 要因:遅延 - 誰に:自分だけ / いつ:常に, 特定の操作をしたとき - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:アニメーション・音・エフェクトは押した瞬間に開始(先行演出)、結果(ダメージ・報酬)だけをサーバーの確定後に表示、移動・通常攻撃は予測してすぐに反映、サーバーから位置の補正を受けたら、その位置からまだ確認されていない入力を適用し直す。サーバー:受け取った入力で移動を自ら計算し、クライアントが予測した位置との差が基準を超えたときだけ補正値を送る。 - 数値の目安:反応時間 ≈ Ping + ティック間隔の半分 + 1フレーム。1秒20ティック・Ping 150msなら約190ms。 - グラフでは:最初から常に高い(入力から演出開始までの時間、RTT(Ping)) - 確認箇所:開発ビルドのクライアントログにボタン入力の時刻、最初のアニメーション・音の開始時刻、サーバー応答の到着時刻を記録し、ゲーム内のRTTと並べて確認。エンジンのネットワークエミュレーション(Unreal NetEmulation.PktLag)や試験サーバーのLinux tc netemで遅延を加え、Pingを変えながら測定 - 該当する場合:演出の開始が常にサーバー応答の到着と同じ瞬間で、入力から演出までの時間がRTT + ティック待ちの分あり、加えた遅延の分だけそのまま延びる - 該当しない場合:演出は押した瞬間に始まり、ダメージの数字などの結果だけが遅れるなら正常な設計。Pingが低い環境でもティック間隔以上遅れるなら、二重のティック待ちかクライアントのフレームの問題 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:ターン制、カード、放置系のように速い反応が必要ないゲームでは、この方式が最も単純で安全です。問題になるのは、リアルタイムの操作があるゲームで、移動や通常攻撃までこの方式で作った場合です。 - 出典: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · サーバーの結果だけを待つクライアントでは、遅延が500msならすべての行動が500ms後にしか見えない。クライアントサイド予測とサーバー補正で解決 - [Using Gameplay Abilities in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-gameplay-abilities-in-unreal-engine) · Epic Games · Local Predictedは押した瞬間に実行してサーバーが最終決定、Server Initiatedは予測がないため使う本人に遅延が見える - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · クライアントが移動を予測・保存しておき、サーバーとの誤差が許容値(MAXPOSITIONERRORSQUARED)を超えたときだけ補正し、補正後に保存しておいた移動を適用し直す - [Using Network Emulation in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-network-emulation-in-unreal-engine) · Epic Games · サーバー・クライアントに最小・最大の遅延とパケットロス率を設定して試験、コンソールではNetEmulation.PktLagのように設定 - [tc-netem(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-netem.8.html) · iproute2 · 送信パケットに遅延・ジッター(delay TIME JITTER)とパケットロス(loss random PERCENT)を加え、実際のネットワークを模倣する試験ツール #### sy-chatty · 逐次往復の多いプロトコル(chatty) · Chatty protocol / sequential round trips 1回の操作でサーバーとの往復が順番に何回も必要になると、Pingがその回数分だけ積み重なります。 - なぜ → すると → 画面では:ショップを開く → 一覧を要求 → 価格を確認 → 購入 → インベントリ更新を、それぞれ別々にリクエスト → 前のリクエストの応答を受け取ってから次のリクエストを送る → Ping 150msで1回の購入に1秒近くかかる。ロードがやけに長い - 症状:入力遅延, 接続不可・無限ロード / 要因:遅延 - 誰に:特定の機能だけ, 自分だけ / いつ:特定の操作をしたとき, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:複数の段階を1回のリクエスト・レスポンスにまとめるようプロトコルを変更(例:購入の応答に更新後のインベントリを含める)。クライアント:必要なデータを先に受け取っておく、結果を待たないUI。 - 数値の目安:かかる時間 ≈ 往復回数 ×(Ping + サーバー処理 + ティック待ち)。5回ならPing 150msで約0.85〜1秒。 - グラフでは:最初から常に高い(機能ごとの完了時間、1回の操作あたりの往復回数) - 確認箇所:サーバー側のパケットキャプチャ(Wireshark)で、試験アカウントでショップでの購入・ログインなどの操作を1回行う間に、リクエストとレスポンスが何回交互にやり取りされるかとその間隔を数える。サーバーのリクエストログがあれば、セッションIDでまとめてリクエスト数と、リクエストごとの到着・応答時刻を確認 - 該当する場合:1つの操作でリクエストが前の応答を待ってから順番に何回もやり取りされ、完了時間がおおよそ往復回数 × RTTで、Pingが高い地域のユーザーほど同じ機能が比例して遅い - 該当しない場合:往復は1〜2回なのに1つの応答に時間がかかるなら、サーバー処理・DB側の原因。Pingに関係なく全ユーザーが同じように遅いなら、サーバー負荷を確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Chatty I/O antipattern](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/) · Microsoft Azure · 小さなI/Oリクエストが多いと、累積した遅延で応答性が大きく落ちる。リクエストをより大きく、少なくまとめるよう推奨 #### sy-no-queue · スキルの先行入力なし · No input/spell queue 前のスキルがサーバーで終わったという確認を受けるまで次のスキルを押せないと、連携のたびに往復時間が挟まります。 - なぜ → すると → 画面では:次のスキル入力を「前のスキルの確定後」にだけ受け付ける → スキルとスキルの間に、毎回Pingの分だけ空白の時間ができる → 連携の間に毎回すき間ができ、Pingが高いほどDPSが下がる - 症状:入力遅延, 不発・ロールバック / 要因:遅延 - 誰に:自分だけ, 特定の機能だけ / いつ:特定の操作をしたとき - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:クールタイムが終わる前の一定時間(例:0.3〜0.4秒)以内の入力も受け付けてすぐサーバーへ送る、先行入力の受付時間。サーバー:少し早く届いた入力を拒否せず、クールタイムが終わった瞬間に実行。 - 数値の目安:クールタイム1秒の連携でPingが150msなら、スキルの間が毎回0.15秒以上空き、同じ時間に使えるスキルが13%以上減ります。 - グラフでは:最初から常に高い(スキル間の空白時間、RTT(Ping)) - 確認箇所:サーバーログに、キャラクターごとのスキルのクールタイムが終わった時刻、次のスキルのリクエストが届いた時刻、実行時刻を記録し、その間の空白時間をユーザーのRTTと比較 - 該当する場合:クールタイムが終わってから次のスキルが実行されるまで常にRTT程度の空白があり、Pingが高いユーザーほど空白時間が長く、同じ時間に使ったスキル数が少ない - 該当しない場合:空白時間がPingに関係なく一定なら、共通クールタイム(GCD)やアニメーションの長さの設計。空白時間がときどきだけ大きく跳ねるなら、ジッター・パケットロスかティックバジェット超過を確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:たとえばWorld of Warcraftは先行入力の受付時間を設け、プレイヤーが設定で調整できるようにしました。受付時間が往復時間より長ければ、連携の間にPingがほとんど挟まりません。 - 出典: - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · EverQuest IIでは、遅延が大きくなるほどキャラクターが与えるダメージが減って戦闘が長引いた(0→500msで約2分の戦闘が5秒延長) #### sy-short-window · Pingに削られる短い判定の受付時間 · Timing window too short for latency + reaction 回避・パリィ・ガードのように反応すべき時間が短いと、Pingがその時間を削ってしまい、よけられない攻撃が生まれます。 - なぜ → すると → 画面では:ボスの攻撃の予兆0.5秒、パリィの判定0.2秒のような短い判定の受付時間 → 予兆を見るのが遅れ(下りの遅延 + 補間)、自分の入力も遅れて届く(上りの遅延 + ティック待ち) → 確かによけたのに当たる、パリィが不発になる - 症状:不発・ロールバック, 入力遅延 / 要因:遅延 - 誰に:自分だけ, 特定の機能だけ / いつ:特定の操作をしたとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発, インフラチーム・サーバーインフラ - ゲーム開発チームの対応:サーバー:攻撃の予兆をサーバー時刻で予約して前もって送る、判定の受付時間をPingの分だけ延ばす(ラグコンペンセーション)。クライアント:受け取った予兆を予約されたサーバー時刻に合わせて再生。 - インフラチームの対応:ユーザーが多い地域の近くにサーバーを配置(地域サーバー)し、Ping自体を減らす。 - 数値の目安:Ping 150ms、補間100msなら、予兆が自分の画面に出るまで約0.18秒、自分の入力がサーバーに届くまで約0.1秒かかります。人の反応時間0.25秒を足すと、0.5秒の予兆に反応するのはほぼ不可能です。 - グラフでは:一部だけ高い(回避・パリィの失敗率(Ping帯別)) - 確認箇所:サーバーログに判定の受付時間の開始・終了時刻、ユーザー入力のサーバー到着時刻、そのユーザーのRTTを記録し、失敗率をPing帯(例:50ms刻み)ごとに分けて確認 - 該当する場合:Pingが高い帯ほど失敗率がはっきり高く、失敗した入力が受付時間の終了から少し後(RTTと補間時間を足した値以内)に届いている - 該当しない場合:Ping帯に関係なく失敗率が同程度なら、攻撃パターンの難易度の問題。入力が受付時間内に届いているのに失敗と出るなら、判定コードかサーバー側の検証を確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · 単純反応時間の平均は約231ms(機器の遅延を補正すると213ms)、近年の大規模研究では233〜400ms - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · 精密で締め切りが短い行動ほど遅延に敏感(一人称で約100ms、三人称で約500ms、全体を見渡す俯瞰視点で約1,000msが限界) - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · サーバー時刻(ServerTime)に合わせてイベントを予約し、全クライアントが同じ瞬間に再生する方法 #### sy-no-lagcomp · ラグコンペンセーションのない判定 · Server-now hit validation サーバーが「今のサーバー上の位置」だけで命中を判定すると、自分が見た画面と判定が食い違います。 - なぜ → すると → 画面では:自分の画面の相手は約0.2秒前の位置(Ping 150ms、補間100msの場合) → サーバーは現在位置で判定するため、自分が狙った場所にはもういない → 確かに当てたのに外れる。動く対象には偏差撃ちが必要 - 症状:不発・ロールバック / 要因:遅延 - 誰に:自分だけ / いつ:特定の操作をしたとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:攻撃者が見ていた時点に巻き戻して判定(ラグコンペンセーション)、またはターゲット指定方式に変更。クライアント:攻撃時に自分が見ていた時点(補間中のサーバー時刻)も一緒に送る。 - グラフでは:一部だけ高い(動く対象への命中率(Ping帯別)) - 確認箇所:サーバーの判定ログに、攻撃時刻、攻撃者の画面上の対象位置(クライアントが送った値)、判定に使ったサーバー上の対象位置、攻撃者のRTTを一緒に記録。開発ビルドでサーバーが判定に使った位置をクライアント画面に重ねて描くと、すぐにわかる - 該当する場合:外れた判定での2つの位置の差がおおよそ対象の速度 ×(攻撃者のRTT + 補間時間)で、Pingが高いほど動く対象の命中率だけが下がる - 該当しない場合:止まっている対象にも外れるなら、ヒットボックス・衝突判定の問題。巻き戻しをしているのに食い違うなら、クライアントが補間時間をサーバーに誤って伝えていないかを確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · ラグコンペンセーションがないと、遅延の分だけ偏差撃ちが必要。サーバーが遅延と補間時間の分だけ巻き戻して判定するラグコンペンセーション - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · サーバーが射撃の瞬間にプレイヤーが見ていたゲーム状態へ巻き戻して命中を判定、クライアントは見ていたシミュレーション時刻も一緒に送る - [Source SDK 2013: player_lagcompensation.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player_lagcompensation.cpp) · Valve · Sourceエンジンの巻き戻し時間 = ネットワーク遅延 + 補間時間 #### sy-lagcomp-overreach · 過剰なラグコンペンセーション · Excessive lag compensation 攻撃者を基準に巻き戻しすぎると、撃たれる側はもう隠れたのに当たります。 - なぜ → すると → 画面では:Pingが高い攻撃者のために、サーバーが大きく巻き戻して判定 → 撃たれる側の画面では、すでに遮蔽物に隠れた後 → 「壁の後ろで撃たれた」、Pingが高い人が有利 - 症状:不発・ロールバック / 要因:遅延 - 誰に:自分だけ, 特定の地域・ISP / いつ:特定の操作をしたとき - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:巻き戻しの上限を設ける(例:200〜250ms)、それよりPingが高い攻撃者は上限まで巻き戻し、残りは自分で偏差撃ちさせる。 - グラフでは:一部だけ高い(命中ごとの巻き戻し時間(攻撃者のPing別)) - 確認箇所:サーバーの判定ログに、命中ごとの巻き戻し時間、攻撃者のRTT、撃たれる側が遮蔽物に入ったサーバー時刻を記録。開発ビルドで巻き戻したヒットボックスを画面に描いてみる(Sourceエンジンではsv_showlagcompensation) - 該当する場合:「壁の後ろで撃たれた」という報告の命中が巻き戻し時間の長い攻撃者に集中し、巻き戻し時間が上限なく攻撃者のPingに合わせて延びている - 該当しない場合:巻き戻し時間が短い命中でも壁の後ろでの被弾が起きるなら、ヒットボックス・衝突判定の問題。撃たれる側のPingが高いなら、その人の移動がサーバーに遅れて届いたために起きたこと - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:巻き戻しによる判定は「撃った側優先」です。撃たれる側が自分の画面ですでに安全な場所に入っていたなら巻き戻さない、「撃たれる側優先」の例外も提案されています。 - 出典: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · 巻き戻しに上限がないと、遅延500msの人が遮蔽物に隠れた0.5秒後の相手にも当てられるため、上限を設ける - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · 「角の向こうで撃たれる(shot around the corner)」現象、商用FPSの巻き戻し上限、撃たれる側が安全なら巻き戻さない手法の提案 - [Source SDK 2013: player_lagcompensation.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player_lagcompensation.cpp) · Valve · Sourceエンジンの巻き戻し上限sv_maxunlagはデフォルト1秒(最大1秒)、sv_showlagcompensationは巻き戻したヒットボックスを画面に表示 #### sy-client-auth · クライアント権威型 · Client-authoritative results それぞれが自分の結果を決めると、自分の画面は快適ですが、ほかの人の画面と結果が食い違い、チートにも弱くなります。 - なぜ → すると → 画面では:位置・命中をクライアントが決め、サーバーは中継するだけ → 2人が互いに自分が先に当てたと主張し、サーバーは検証できない → 相手がワープ・壁抜け、「自分は当てたのに当たっていない」 - 症状:ワープ, 不発・ロールバック / 要因:遅延 - 誰に:サーバー全体 / いつ:常に - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:重要な結果(命中など)は自ら検証、移動は速度・距離をチェック。クライアント:サーバーが拒否・補正した結果を受け取ったら、その値に戻す。 - グラフでは:最初から常に高い(ありえない移動速度・食い違う命中報告の数) - 確認箇所:サーバーで、クライアントが報告した位置・命中をそのまま記録しておき、連続する位置報告から移動速度を計算して、最大速度を超える報告と、2人が互いに先に当てたという報告を数える - 該当する場合:サーバーが報告を検証せずにほかのクライアントへ中継しており、ありえない速度や互いに食い違う命中報告が、アップデート・地域に関係なく継続的に出る - 該当しない場合:サーバーが結果を自ら計算・検証しているなら、この原因ではない。その場合のワープはパケットロスか補間バッファ側を確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · クライアントが結果を報告する方式は、クライアントを信頼できる場合にだけ成り立つ。チートの懸念からサーバー権威型を採用 - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · サーバー権威モデル:サーバーはクライアントが見たゲーム状態を決して信用しない - [Distributed authority topologies (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/distributed-authority.html) · Unity · 権限をクライアントに分散するとチートが容易になり、すべてのオブジェクトを管理する単一のシミュレーションがなくなる #### sy-lockstep · ロックステップでの最も遅いプレイヤー待ち · Lockstep waits for the slowest peer 全員が同じターンを一緒に計算する構造では、1人の入力が遅れると全員が待たされます。 - なぜ → すると → 画面では:ターンごとに全プレイヤーの入力がそろわないと計算できない → 1人の入力がジッター・パケットロスで遅れて到着 → 全員が同時に一瞬止まり、ひどいと「プレイヤーを待っています」ウィンドウ - 症状:フリーズ, カクつき, 入力遅延 / 要因:ジッター, パケットロス, ストール - 誰に:特定の場所・チャンネル / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:入力遅延をPingに合わせて自動調整、遅れている人だけを一時的に切り離し、残りは待たずに進行。クライアント:決められた入力遅延を適用、中継サーバーのないP2Pなら、入力遅延の調整と遅れている人の処理もホストのクライアントが担う。 - 数値の目安:入力遅延を「入力が相手に届く時間 + ジッター」より短く設定すると、停止が頻発します。届く時間は、直接やり取りするならPingの半分、中継サーバーを経由するなら2人のPingを足した値の半分ほどです。 - グラフでは:不定期なスパイク(ターンの待ち時間、プレイヤーごとの入力到着の遅れ) - 確認箇所:ターンごとにプレイヤー別の入力到着時刻と、ターンが止まって待った時間を記録し、止まったターンで誰の入力を待っていたかを確認。中継サーバーがあれば、サーバー側のパケットキャプチャでプレイヤー別の入力パケットの到着間隔からも確認できる - 該当する場合:止まったターンのたびに同じ1人の入力が入力遅延より遅れて到着しており、その時刻にその人のジッター・パケットロスが跳ねている - 該当しない場合:入力はすべて時間どおりに届いているのに止まるなら、最も遅いPCの計算時間かサーバー処理の問題。止まらずに2つの画面の結果だけが食い違うなら計算結果のずれ(desync)なので、コマンド同期の経路計算の不一致を確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · フレームnの入力がすべて届かないと計算できないため、遅れると待つ。ジッターを吸収する再生遅延バッファが小さいと一瞬止まる - [1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond](https://www.gamedeveloper.com/programming/1500-archers-on-a-28-8-network-programming-in-age-of-empires-and-beyond) · Game Developer · コマンドを2ターン後に実行するよう予約し、最も遅いコンピューターとPingに合わせてターンの長さを調整(Speed Control) - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · 入力遅延(incoming delay)を「A→サーバー + サーバー→B」の遅延の分だけ設け、全員が同じ瞬間に適用 #### sy-rollback · ロールバックネットコードの予測失敗 · Rollback misprediction 相手の入力を予測して先に見せ、外れたら巻き戻して計算し直します。Pingが大きいほど巻き戻す幅が大きくなります。 - なぜ → すると → 画面では:相手が入力を変える(予測と違う) → 実際の入力がPingの半分だけ遅れて届き、その分を巻き戻して再計算 → 相手の動きが数フレーム飛んだり、急に変わったりする - 症状:ワープ / 要因:遅延, ジッター - 誰に:自分だけ / いつ:特定の操作をしたとき, ときどきランダムに - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:入力遅延を1〜3フレーム組み合わせて巻き戻し幅を減らす、巻き戻しの上限を設ける。 - 数値の目安:Ping 100ms(片道50ms)なら、60fpsで約3フレーム巻き戻します。入力遅延を2フレーム設けると1フレームに減ります。 - グラフでは:不定期なスパイク(巻き戻したフレーム数、RTT(Ping)) - 確認箇所:クライアントが巻き戻しのたびに、巻き戻したフレーム数、その時のRTT、入力遅延の設定、巻き戻し・再計算にかかった時間を記録 - 該当する場合:相手の動きが飛んだという瞬間に巻き戻したフレーム数が大きく、平均の巻き戻し幅がおおよそ(片道遅延 − 入力遅延)÷ フレーム時間で、Pingが大きいほど大きくなる - 該当しない場合:巻き戻し幅が小さいのにカクつきが起きるなら、再計算が1フレームの時間を超える性能の問題。巻き戻した後も2つの画面の結果が食い違い続けるなら計算結果のずれ(desync) - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [GGPO Rollback Networking SDK](https://www.ggpo.net/) · GGPO · 相手の入力を予測して先に進め、実際の入力が違えば、ずれた時点から現在までを計算し直す - [8 Frames in 16ms: Rollback Networking in 'Mortal Kombat' and 'Injustice 2'](https://www.gdcvault.com/play/1025471/8-Frames-in-16ms-Rollback) · GDC · ロールバックでロックステップのローカル入力遅延をなくし、16ms以内に最大8フレームを巻き戻して再計算 #### sy-no-timestamp · タイムスタンプなしの受信即時再生 · Events played on arrival (no timestamps) サーバーのイベントに発生時刻を付けず、受け取ったらすぐに再生すると、ネットワークのジッターがそのまま演出のタイミングのばらつきになります。 - なぜ → すると → 画面では:「攻撃開始」「エフェクト再生」のイベントを届いた瞬間に実行 → パケットごとに到着時間が違うため、間隔がばらつく → 連続攻撃のモーションが速くなったり遅くなったりし、ボスの攻撃パターンのタイミングが毎回違う - 症状:カクつき, 早送り / 要因:ジッター - 誰に:自分だけ / いつ:常に - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:イベントに付いた時刻に合わせて再生(イベント予約・補間バッファ)。サーバー:イベントに発生時刻(サーバー時刻)を付けて送る。 - グラフでは:不定期なスパイク(イベントの再生間隔、パケットの到着間隔) - 確認箇所:サーバーログのイベント発生時刻と、クライアントログの到着・再生時刻をイベント番号で突き合わせて間隔を比較。開発ビルドでジッターを加えて(tc netemのジッター値、Unrealのネットワークエミュレーションの最小・最大遅延)再現してみる - 該当する場合:サーバーでの発生間隔は一定なのに、再生間隔が到着間隔をそのままなぞってばらつく - 該当しない場合:到着間隔はそろっているのに再生がばらつくなら、クライアントのフレームの問題(フレームタイムのスパイク)。サーバーでの発生間隔からぶれているならティックバジェット超過 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · 更新ごとにサーバー時刻を付け、現在時刻から補間時間(100ms)を引いた目標時刻の位置で描く - [Snapshot Interpolation](https://gafferongames.com/post/snapshot_interpolation/) · Gaffer On Games · 受け取ったスナップショットをすぐ描くとジッターで途切れ、補間バッファに少しためてから描くとなめらか - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · RPCに送信時刻を入れ、受信側がサーバー時刻に合わせてエフェクトを再生する例 - [tc-netem(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-netem.8.html) · iproute2 · 送信パケットに遅延・ジッター(delay TIME JITTER)とパケットロス(loss random PERCENT)を加え、実際のネットワークを模倣する試験ツール - [Using Network Emulation in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-network-emulation-in-unreal-engine) · Epic Games · サーバー・クライアントに最小・最大の遅延とパケットロス率を設定して試験、コンソールではNetEmulation.PktLagのように設定 #### sy-double-tick · 二重のティック待ち · Double tick quantization リクエストを次のティックまでためてから処理し、結果もその次のティックで送ると、ティック間隔が2回分加わります。 - なぜ → すると → 画面では:受け取ったリクエストは次のティックで処理 → 処理結果も次の送信ティックにまとめて送る → 回線のPingは低いのに、反応がティック間隔の1.5倍ほど一定に遅れる。10ティックのサーバーなら平均0.15秒、最悪0.2秒 - 症状:入力遅延 / 要因:遅延 - 誰に:サーバー全体 / いつ:常に - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:処理したティックですぐ応答を送る、ティックレートを上げる、重要な応答は即時送信。 - 数値の目安:10ティックのサーバーは1ティックが100msなので、ティック待ちだけで平均150ms、最悪200msが加わります。待つのが1回だけなら平均50msです。 - グラフでは:最初から常に高い(リクエスト到着から応答送信までの時間) - 確認箇所:サーバー側のパケットキャプチャで、試験アカウントが同じ行動(例:アイテム使用)を何回か行うとき、リクエストパケットが届いた時刻とその応答パケットが出た時刻の間隔を測る。サーバーログがあれば、リクエスト到着時刻、処理したティック番号、応答を送った時刻を確認 - 該当する場合:サーバー内でかかった時間が平均でティック間隔の1.5倍、最大で2倍ほどで、RTTに関係なく一定 - 該当しない場合:サーバー内の時間が平均でティック間隔の半分前後なら、ティック待ちは1回だけ。ティック間隔よりばらついて長いならティックバジェット超過を確認 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · 届いた入力はティックの境界まで最大1ティック待ち、適用・送信にさらに1フレームかかる。ティックレートが高いほど短くなる - [VALORANT's 128-Tick Servers](https://www.riotgames.com/en/news/valorants-128-tick-servers) · Riot Games · 遅延の一部はネットワーク、一部はサーバーのティックレートから来る - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · NetworkVariableの変更はすぐには送らず、ネットワークティックごとにまとめて送信 #### sy-strict-check · 厳しすぎるサーバー検証 · Over-strict server validation 移動速度・クールタイム・射程をサーバーが厳しくチェックしすぎると、ジッターでまとめて届いた正常な入力まで拒否します。 - なぜ → すると → 画面では:「1ティックで移動できる距離」「クールタイムの許容誤差0ms」のような厳しい基準 → ジッターで2つのコマンドが1ティックにまとめて届くと、ルール違反と判定 → 引き戻し、クールタイムが終わったのにスキルが拒否される - 症状:引き戻し, 不発・ロールバック / 要因:ジッター - 誰に:自分だけ / いつ:ときどきランダムに, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:累積の許容量(トークンバケット)方式でチェック、Ping・ジッターの分の余裕を持たせる。 - グラフでは:不定期なスパイク(サーバー検証による拒否・位置補正の数) - 確認箇所:サーバーログに、検証による拒否・位置補正のたびに、理由、そのティックに届いたそのユーザーのコマンド数、直前のコマンドとの到着間隔を記録 - 該当する場合:拒否・補正が、1ティックにコマンドが2つ以上まとめて届いた瞬間に集中しており、数秒単位で合計した移動量・使用回数はルールの範囲内 - 該当しない場合:数秒単位で合計してもルールを超えるなら、実際の速度超過・チートの可能性。拒否が特定の通信事業者と夜の時間帯に集中するなら、特定の通信事業者のユーザーに集中する検証の誤検知側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · ティックごとにたまるコマンド処理バジェット(最大sv_maxusrcmdprocessticks 24ティック)で、まとめて届いたコマンドを許容。これ以上厳しく制限すると正常なユーザーでもカクつきが出たという開発者のコメント - [RFC 2697: A Single Rate Three Color Marker](https://www.rfc-editor.org/rfc/rfc2697) · IETF · トークンバケット:平均速度(CIR)と一度に許容するバーストサイズ(CBS)で判定 #### sy-host · ホスト(部屋主)型の構成 · Listen server / host advantage 1人のプレイヤーのPCがサーバーの役割をすると、その人の回線とPC性能が全員の体感を決めます。 - なぜ → すると → 画面では:ホストのPCがサーバーの役割を担う(P2P、リッスンサーバー) → ホストの回線やPCが遅いと全員に波及、ホスト自身はPing 0 → ホストだけが有利、ホストが抜けると全員がフリーズ・切断 - 症状:カクつき, フリーズ, 切断 / 要因:遅延, ストール - 誰に:特定の場所・チャンネル / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発, インフラチーム・サーバーインフラ - ゲーム開発チームの対応:サーバー:判定を担う専用サーバーへ移行、それまではマッチング時に回線・PCの良い人をホストに選ぶ。クライアント:ホストの移行(マイグレーション)に対応、マッチング時にほかの参加者とのPing・上り速度・PC性能を測定して送る。 - インフラチームの対応:専用サーバー用のサーバー機器・インスタンスを確保、ユーザーが多い地域の近くに配置。 - グラフでは:一部だけ高い(ホストごとのラグ報告・切断数) - 確認箇所:マッチログに、ホストの上り速度、参加者ごとのホストとのRTT、ホストPCのフレーム時間、ホストが抜けた時刻を記録し、ラグ・切断の報告をホストごとにまとめて確認。ユーザー側でも、同じメンバーでホストだけ替えてもう一度プレイすれば確認できる - 該当する場合:ラグ・切断が特定のホストの部屋に集中し、そのホストの上り速度が低いときやフレーム時間が長いときに参加者全員が一緒に悪化し、ホストの移行がなければホストが抜けた瞬間に全員が切断される - 該当しない場合:ホストに関係なく同じ地域の参加者だけが悪いなら、回線・経路の問題。専用サーバー構成なら、この原因ではない - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Networking Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-overview-for-unreal-engine) · Epic Games · リッスンサーバーのホストはほかのクライアントより有利で、サーバーとレンダリングを兼ねるため負荷が大きい - [Distributed authority topologies (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/distributed-authority.html) · Unity · セッションの所有者が抜けると、残ったクライアントの中から新しい所有者を自動で選出 - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · P2Pゲームも、ホストがサーバーの役割を兼ねるクライアント・サーバー構成とみなせる #### sy-optimistic-reject · 先行演出後のサーバー拒否 · Client-side feedback rejected by server 自分の画面で先に見せたヒット・スキルを後からサーバーが認めないと、確かに見た結果がなかったことになります。 - なぜ → すると → 画面では:ヒットエフェクト・スキルモーションをサーバーの確認前に先に再生(先行演出) → サーバーが射程・対象の位置・クールタイム・リソースを改めてチェックして拒否 → 血しぶきが出たのにダメージなし、スキルのモーションだけ出て効果なし、クールタイムだけ回る - 症状:不発・ロールバック, 引き戻し / 要因:遅延 - 誰に:自分だけ, 特定の機能だけ / いつ:特定の操作をしたとき - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:ダメージの数字・死亡・報酬など確定が必要な部分だけサーバーの結果で表示、よくある拒否理由は先にチェック、拒否されたらクールタイム・リソースを元に戻して理由を表示。サーバー:射程・対象位置のチェックにPingの分の余裕を持たせる、拒否の応答に理由を含めて送る、スキルごとの拒否率をメトリクスとして収集。 - 数値の目安:拒否の応答は、押してからPing + ティック待ちの分だけ遅れて届きます。Ping 150msなら、約0.2秒の間「当たった」と思い込みます。 - グラフでは:一部だけ高い(スキルごとのサーバー拒否率(Ping帯別)) - 確認箇所:サーバーで、スキルごとの拒否率と拒否理由(射程、対象位置、クールタイム、リソース)を集め、ユーザーのRTT帯別に分ける。クライアントには、先行演出した行動が拒否された回数を記録 - 該当する場合:拒否が特定のスキルと射程・対象位置の理由に集中し、Pingが高いほど拒否率が上がる - 該当しない場合:拒否理由がクールタイム・リソースでPingと関係ないなら、クライアントとサーバーのデータ値(クールタイム、コスト)が違っていないかを確認。拒否はなく、演出がサーバー応答の後にしか始まらないなら、サーバー応答後にだけ演出(リクエスト・レスポンス方式) - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:先行演出はPingを隠す最もよい方法です。ただし、クライアントとサーバーが判断に使う情報(相手の位置、残りのリソース)が違うほど、拒否が増えます。スキルごとの拒否率をメトリクスとして集めておくと、判定が食い違う箇所を見つけやすくなります。 - 出典: - [Using Gameplay Abilities in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-gameplay-abilities-in-unreal-engine) · Epic Games · Local Predictedのアビリティはクライアントで即座に実行するが、サーバーが最終決定し、結果を覆すことがある - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · 武器の発射をクライアントで予測してエフェクトを先に再生し、サーバーの結果で予測の誤差を修正 #### sy-path-mismatch · コマンド同期の経路計算の不一致 · Command sync with divergent pathing 「ここへ行け」だけをやり取りして経路は両側でそれぞれ計算すると、計算が少し違うだけで、キャラクターやモンスターが別の経路を進んだ後、元の位置に引き戻されます。 - なぜ → すると → 画面では:クリック移動・モンスターの追跡で目的地だけを送り、経路はクライアントが別途計算 → 地形データの違い、ほかのキャラクターとの衝突、計算順序の違いで、サーバーと別の経路を移動 → モンスターが壁をすり抜けて進んだ後にパッと位置が移る、クリックしたキャラクターが滑るように向きを変える - 症状:ワープ, 引き戻し / 要因:遅延 - 誰に:特定の場所・チャンネル, 自分だけ / いつ:移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:経路の中間地点(ウェイポイント)も一緒に送る、定期的に位置を合わせる。クライアント:ずれはなめらかに収束させる、サーバーと同じ地形データを使う。 - グラフでは:不定期なスパイク(オブジェクトごとの位置補正の回数・距離) - 確認箇所:サーバーが送った位置とクライアントが計算した位置の差をオブジェクトごとに記録し、補正が起きた座標をマップ上にプロットしてみる。両側の経路の結果や位置をチェックサムにまとめて定期的に比較すれば、ずれ始めた時点を見つけられる - 該当する場合:補正が特定の地形(段差、狭い通路、坂)や混雑した場所に集中し、ネットワークのメトリクスが正常なユーザーでも同じ場所で繰り返し起きる - 該当しない場合:場所に関係なく、パケットロス・ジッターが跳ねた瞬間にだけ補正されるなら回線の問題。1匹のモンスターが複数の人の画面で同時に飛ぶなら、モンスターの制御権が遅いクライアントにないかを確認 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:クリック移動・タブターゲットのゲームがPingに鈍感な理由の1つがこの方式です。その代わり両側の結果が同じになる保証がないので、ときどき位置を合わせる仕組みが欠かせません。浮動小数点演算は、CPUの種類、コンパイラとその最適化設定(デバッグビルドとリリースビルドの違いを含む)によって結果がわずかに変わることがあります。ロックステップ・ロールバックのように入力だけをやり取りし、両側の計算結果がまったく同じだと仮定する構造では、この小さな差が積み重なり、2つの画面のゲーム状態が分かれてしまうずれ(desync)が起きることがあります。 - 出典: - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · 同じマシンでは決定的でも、コンパイラ・OS・CPUが違えば浮動小数点の結果が変わることがある - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · 入力と一緒に状態も送れば、完全な決定性がなくても両側を合わせられる - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · パケットロスや、2体のキャラクターが同じ場所へ行こうとしたときに、サーバーとクライアントのシミュレーションが食い違い、補正が必要になる - [1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond](https://www.gamedeveloper.com/programming/1500-archers-on-a-28-8-network-programming-in-age-of-empires-and-beyond) · Game Developer · ごく小さな差が時間とともに大きくなり、ワーカーユニットの経路が少しずつずれる。ワールド・オブジェクト・経路探索をチェックサムで比較してずれ(out-of-sync)を見つける - [Floating Point Determinism](https://gafferongames.com/post/floating_point_determinism/) · Gaffer On Games · 同じ浮動小数点のコードでも、コンパイラ・CPUアーキテクチャ、デバッグ・リリースビルドによって結果が変わることがある。AMDとIntelのCPUが超越関数でわずかに異なる値を返した事例 - [/fp (Specify floating-point behavior)](https://learn.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior) · Microsoft · /fp:fastは浮動小数点演算の順序を入れ替えたりまとめたりするため、ほかの/fp設定と結果が変わることがあり、FMAでまとめた演算も、別々に乗算・加算した結果と異なることがある #### sy-low-send-rate · 低いスナップショット送信レート · Low snapshot / update rate サーバーが位置の更新(スナップショット)を1秒に数回しか送らないと、その分だけ補間バッファを長く取る必要があり、ほかのキャラクターをより遠い過去の姿で見ることになります。 - なぜ → すると → 画面では:送信量を節約するため、位置の更新を1秒に5〜10回しか送らない → なめらかに描くにはバッファをパケット間隔の2倍(200〜400ms)取る必要があり、短くするとパケットを1つ落としただけで止まる → 相手の方向転換が遅れて見え、判定と食い違う。バッファが短いとカクつき、パケットロス時にはワープ - 症状:カクつき, ワープ, 不発・ロールバック / 要因:遅延, パケットロス - 誰に:サーバー全体 / いつ:常に - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:近い対象や戦闘中の対象は頻繁に、遠い対象はまれに送る、変わった部分だけを送って(デルタ圧縮)1回のサイズを減らし、送信頻度を上げる。クライアント:補間バッファの長さをパケット間隔に合わせて自動調整。 - 数値の目安:1秒に10回ならパケット間隔100ms、バッファ200ms。Ping 150msの片道遅延75msを足すと、相手を約0.3秒前の姿で見ることになります。 - グラフでは:最初から常に高い(クライアントごとのパケット到着間隔、補間バッファの長さ) - 確認箇所:サーバー側のパケットキャプチャで1人のユーザーへ向かうフローだけに絞り、WiresharkのI/O Graphsで毎秒のパケット数と間隔を確認。ゲーム側のログがあれば、オブジェクトごとの更新間隔と、クライアントの補間バッファの余裕(次のスナップショットが届くまでの残り時間)も合わせて確認 - 該当する場合:位置の更新が毎秒5〜10回(間隔100〜200ms)と常にまばらで、補間バッファを200ms超に設定しているか、バッファの余裕が頻繁に0になる - 該当しない場合:更新は細かく送られているのに到着間隔だけがぶれるなら、ジッター・パケットロス側。混雑時に遠いオブジェクトだけをまれにしか受け取らないなら、接続ごとの送信バジェット・優先度 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · 毎秒10回の更新なら、補間200msで1回の欠落に耐えられる。Half-Lifeのデフォルトは毎秒20回・補間100ms - [Snapshot Interpolation](https://gafferongames.com/post/snapshot_interpolation/) · Gaffer On Games · 毎秒10個なら、2個連続のパケットロスまで耐えるのに350msの遅延が必要、毎秒30個なら150msに減る - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · 優先度の累積で重要なオブジェクトをより頻繁に送り、帯域幅の上限内で残りを順番に送信 - [8.8. The “I/O Graphs” Window](https://www.wireshark.org/docs/wsug_html_chunked/ChStatIOGraphs.html) · Wireshark · 表示フィルターに合うパケット数・バイト数を時間区間ごとのグラフに描く ### 一部のユーザーだけに起きる問題(原因24件) #### pt-slow-burst · 回線が重い人がほかの人の画面でまとめて動く · Laggy player seen by others (bursty inputs) 回線が悪い人の入力は、不規則にまとまってサーバーに届きます。サーバーがティックごとに受け取った分だけ適用すると、ほかの人の目にはそのキャラクターが一瞬止まってから一気に何歩も進んで見えます。 - なぜ → すると → 画面では:回線が重い人の移動コマンドが、あるティックには0個、あるティックには2〜3個ずつ届く → サーバーが受け取ったティックに一度に適用するため、そのキャラクターの位置が階段状に変わる → ほかの人の画面でそのキャラクターだけが一瞬止まっては一気にまとめて移動。ほかは問題なし - 症状:早送り, ワープ / 要因:ジッター, パケットロス - 誰に:特定のキャラだけおかしく見える, 特定の地域・ISP / いつ:常に, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:外部・外部 - ゲーム開発チームの対応:プレイヤーごとの入力バッファでコマンドを均等に分けて適用、入力のシーケンス番号を見て元の間隔どおりに適用、ほかの人の画面の補間バッファを増やすだけでは不十分(サーバーでの位置の記録自体が階段状になっているため)。 - 外部の対応:回線が重いユーザーに、有線接続、Wi-Fi・ルーターの点検を案内。 - 数値の目安:ジッターが80msなら、20ティック(50ms)のサーバーでティックあたりのコマンド数が0〜3個でばらつきます。 - グラフでは:一部だけ高い(プレイヤーごとのティックあたり適用コマンド数、プレイヤーごとのジッター) - 確認箇所:サーバー側のパケットキャプチャで、報告されたユーザーが送ったパケットだけに絞り、ティック間隔(例:50ms)ごとに何個ずつ届いたかを数えてほかのユーザーと比較。サーバーログがあれば、プレイヤーごと・ティックごとに適用した移動コマンド数と入力のシーケンス番号を確認 - 該当する場合:報告されたユーザーのパケットだけがティックごとに0個と2〜3個を行き来してまとまって届き、そのユーザーのジッター・パケットロスが高く、ほかのユーザーのパケットは均等に届いている。そのユーザーが有線に切り替えると減る - 該当しない場合:複数のキャラクターが一斉にまとめて動くなら、サーバーのティック遅延か見ている人側の回線。到着と適用が均等なのにそのキャラクターだけ飛んで見えるなら、見ている側の補間・表示の問題 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:サーバー権威型の構成では、これは正常な動作です。回線が重い1人のラグは、ほかの人には「その人がおかしな動きをする姿」としてだけ見え、ほかの人の操作やモンスターの動きには影響しません。ただし、その人と直接やり取りすること(取引、パーティでのギミック、PvPの判定)は一緒に遅れます。 - 出典: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · サーバーはプレイヤーごとの移動キューに届いた入力をティック順に入れ、空になれば予測で埋める。補正はそのプレイヤーにだけ見え、ほかの人にはなめらかに見える - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · 毎秒60回送ったパケットでも、あるフレームに2個、次のフレームに0個のようにまとまって届く - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · まとまって届いたコマンドをサーバーのティックに分けて処理(meter out) #### pt-event-server · 受信即時処理のサーバーで起きる早送り · Event-driven processing of bursty inputs パケットが届くたびにすぐ処理して通知するサーバーでは、回線が重い人のまとまって届いた行動が立て続けに即実行されます。 - なぜ → すると → 画面では:回線が重い人のスキル・移動のリクエストがまとまって届く → サーバーが受け取った瞬間に順番に実行し、すぐに全員へ通知 → ほかの人の目には、その人がスキルを一瞬で何個も使ったり、早送りのように動いたりする - 症状:早送り / 要因:ジッター - 誰に:特定のキャラだけおかしく見える / いつ:特定の操作をしたとき, 常に - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:行動に付いた入力時刻の間隔どおりに実行(時刻は許容範囲内の場合だけ採用)、またはまとまって届いた行動を拒否せず、最小間隔(共通クールタイム)ずつ空けて順に実行、到着時刻だけを見てクールタイムをチェックしない(正常な入力が不発になる)。クライアント:行動に入力時刻を付けて送る。 - グラフでは:一部だけ高い(プレイヤーごとの行動の実行間隔) - 確認箇所:サーバーログに、プレイヤーごとの行動の到着時刻、実行時刻、クライアントが付けた入力時刻(あれば)を記録し、実行間隔と入力間隔を比較。サーバー側のパケットキャプチャで、そのユーザーのパケットの到着間隔も合わせて確認 - 該当する場合:入力間隔は正常なのに、サーバーでの到着・実行間隔が数msに固まっており、固まった瞬間がほかの人たちからの早送りの報告時刻と重なる - 該当しない場合:入力時刻の間隔から固まっているなら、クライアントかマクロ側。サーバーの実行間隔は均等なのにほかの人の画面でだけ固まって見えるなら、見ている人の回線 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · サーバーはServerMoveを受け取るたびに移動を計算し、直前の移動とのタイムスタンプの差で時間間隔を決める。サーバー時刻との差が大きすぎれば、その移動を破棄 - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · 届いた順に入力を適用すると、60Hzで送っても間隔が均等でないため結果がばらつく #### pt-input-buffer · プレイヤーごとの入力バッファのサイズ · Per-player server input buffer (jitter buffer) サーバーが人ごとに入力を少しためておき、1ティックに1つずつ取り出して使うと、ほかの人の目にはなめらかですが、本人の行動がサーバーで確定するタイミングはその分遅れます。 - なぜ → すると → 画面では:サーバーが回線の重い人の入力をバッファにため、1ティックに1つずつ適用 → バッファが小さいと頻繁に空になり、そのキャラクターがその場に止まるか、サーバーが最後の入力から推測して動かす。大きいと本人の入力の確定が遅れる → 小さいとほかの人の目には一瞬止まって見え、大きいと本人のスキルの結果が遅れて出る(入力遅延) - 症状:カクつき, 入力遅延 / 要因:ジッター - 誰に:特定のキャラだけおかしく見える, 自分だけ / いつ:常に - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:人ごとに回線の状態に合わせてバッファサイズを自動調整、遅れたら2つずつ取り出して追いつく、バッファが頻繁に空になる人のクライアントには入力を早めに送るよう指示。クライアント:サーバーの指示に合わせて入力を少し早めに送る(クライアントの時間調整)。 - 数値の目安:ゲームによって違いますが、通常は1〜3ティック分です。VALORANTは128ティックのサーバーで、サーバー側のバッファを平均半フレーム(約4ms)と、さらに短く保っています。ジッターが大きい人だけバッファを増やす適応型が一般的です。 - グラフでは:一部だけ高い(プレイヤーごとの入力バッファの長さ・空になった回数) - 確認箇所:サーバーに、プレイヤーごと・ティックごとに入力バッファに残っている入力数、バッファが空になって最後の入力から推測して埋めた回数、入力の到着から適用までにかかった時間を記録 - 該当する場合:バッファが小さい人は空になる回数が多く、その瞬間にほかの人の画面で短く止まる。バッファが大きい人は、入力から適用までの時間がバッファの長さの分だけ延びている - 該当しない場合:バッファがほとんど空にならないのにほかの人の画面でカクつきが見えるなら、見ている側の補間の問題。バッファが短いのに入力遅延が大きいなら、RTTそのものか二重のティック待ち - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · サーバーがクライアントの時間基準を調整し、入力キューを、遅延が最小でありながら不規則な到着を吸収できる分だけに保つ。サーバーのバッファリングの目標は平均半フレーム - [NetworkTimeSystem class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkTimeSystem.html) · Unity · LocalBufferSec:サーバーがクライアントのメッセージをバッファリングする時間。クライアントの時間を早めることで、メッセージがサーバーにより早く届く - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · 回線が悪いプレイヤーはバッファの値を増やせる #### pt-isp-validation · 特定の通信事業者のユーザーに集中する検証の誤検知 · Anti-cheat / movement validation false positives on bad ISPs ジッターが大きい回線を使う人は入力がまとまって届くため、サーバーの速度・クールタイムのチェックに頻繁に引っかかります。 - なぜ → すると → 画面では:特定の通信事業者・地域の回線のジッターが夜に大きくなる → まとまって届いた正常な入力を、サーバーが速度超過・クールタイム違反と判断 → その通信事業者のユーザーだけ引き戻し、スキル拒否、ひどいとサーバーから追い出されて切断 - 症状:引き戻し, 不発・ロールバック, 切断 / 要因:ジッター - 誰に:特定の地域・ISP, 自分だけ / いつ:夜のピーク時間帯, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:チェックは数秒にわたる累積の許容量で行う、回線の状態(Ping・ジッター)を参考に基準を緩和、強制切断の前に警告段階を設ける、プレイヤーごとの入力バッファでまとまって届いた入力をティックごとに均等に分け、誤検知そのものを減らす。 - インフラチームの対応:通信事業者ごとのパケットロス率・ジッターの分布を時間帯別に確認してゲーム開発チームに共有、その通信事業者の区間の経路を点検(双方向のmtr)、必要なら経路変更・通信事業者へのエスカレーション。 - グラフでは:特定の時間帯だけ高い(通信事業者(ASN)ごとの検証拒否・強制切断の数、通信事業者ごとのジッター) - 確認箇所:サーバーの検証拒否・補正・強制切断のログに接続元IPの通信事業者(ASN)と時刻を付け、通信事業者・時間帯ごとに集計。インフラチームは同じ時間帯にその通信事業者に向けて双方向のmtrを実行し、ジッター・パケットロスを確認 - 該当する場合:拒否・強制切断が特定の通信事業者に集中して夜に増え、同じ時間帯にその通信事業者のジッターも高く、数秒単位で合計した移動量はルールの範囲内 - 該当しない場合:通信事業者に関係なく特定のアカウントだけで繰り返すなら、実際のチートの可能性。すべての通信事業者で一緒に増えるなら、サーバーのティックが遅れてコマンドがまとめて適用されるサーバー側の原因(ティックバジェット超過) - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · ティックごとにたまるコマンドバジェットで速度超過を防ぐが、より厳しい制限は正常なユーザーにもカクつきを生むという開発者のコメント - [RFC 2697: A Single Rate Three Color Marker](https://www.rfc-editor.org/rfc/rfc2697) · IETF · トークンバケット:平均速度と許容バーストサイズで判定 - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · クライアント・サーバーのタイムスタンプの差が大きければ、移動を破棄するか時間差の解消手順で処理、サーバー時刻で計算してスピードハックを防止 #### pt-raid-member · 回線が重いパーティメンバー1人とボスのギミック · One laggy member in a synchronized mechanic 全員が決まった瞬間に一緒に反応しなければならないレイドのギミックでは、回線が重い1人の遅れた反応がパーティ全体の失敗になります。 - なぜ → すると → 画面では:「全員同時に散開」「1人がボタンを押す」のような全員で対処するギミック → 回線が重い人は予兆を見るのが遅れ、入力も遅れて届く → その1人のせいで全滅、ほかのパーティメンバーは「ラグい人のせい」と感じる - 症状:不発・ロールバック, 入力遅延 / 要因:遅延 - 誰に:特定の場所・チャンネル, 特定のキャラだけおかしく見える / いつ:人が集中したとき, 特定の操作をしたとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:ギミックの判定の受付時間をPingの分だけ余裕を持たせる、予兆はサーバー時刻で前もって送る、1人の失敗が全滅につながらない設計。クライアント:受け取った予兆をサーバー時刻に合わせて再生。 - グラフでは:一部だけ高い(ギミック失敗を招いたプレイヤーごとのRTT) - 確認箇所:サーバーのギミックログに、失敗を招いたプレイヤー、その人の入力の到着時刻、判定の受付時間、その人のRTT・パケットロスを記録 - 該当する場合:全滅を招いた入力のほとんどが同じ1人のもので、その人のRTTがパーティの平均よりはっきり高く、入力が受付時間の直後に届いている - 該当しない場合:失敗がパーティメンバーに均等に分かれているなら、受付時間そのものが短い問題(Pingに削られる短い判定の受付時間)。回線が重い人の入力が受付時間内に届いているのに失敗するなら、サーバーの判定コード - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · サーバー時刻に合わせてイベントを予約し、全員が同じ瞬間に再生する方法 - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · 単純反応時間の平均は約231ms #### pt-mob-control · モンスターの制御権が遅いクライアントにある · Monster movement delegated to a player client サーバー負荷を減らすため、モンスターの移動計算を近くのプレイヤー1人のクライアントに任せるゲームがあります。その人の回線が悪いと、そのモンスターが全員の画面でおかしな動きをします。 - なぜ → すると → 画面では:サーバーがモンスターの移動計算を、最も近い(または先に来た)プレイヤーのクライアントに任せる → 任された人の結果報告が遅れたり、まとまったりしてサーバーに届く → そのモンスターだけが周囲全員の画面で一瞬止まってはワープ。任された本人の画面では問題なし - 症状:ワープ, カクつき, 早送り / 要因:ジッター, パケットロス - 誰に:特定のキャラだけおかしく見える, 特定の場所・チャンネル / いつ:常に, ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:制御権を回線の良い人に渡す(Ping・パケットロス基準)、報告が途切れたらサーバーが即座に回収、ボスのような重要なモンスターはサーバーが自ら計算。 - グラフでは:一部だけ高い(モンスターごとの位置報告の間隔(制御権を持つクライアント別)) - 確認箇所:サーバーに、モンスターごとに制御権を持つクライアントと、そのクライアントの報告間隔・RTT・パケットロスを記録。サーバー側のパケットキャプチャで、そのクライアントが送ったパケットの到着間隔も確認できる - 該当する場合:おかしく動くモンスターの制御権がすべて同じ1人にあり、その人の報告間隔がばらつくか途切れ、制御権をほかの人に渡すとすぐ正常になる - 該当しない場合:サーバーが自ら計算するモンスターも同じように飛ぶなら、サーバーのティック遅延か見ている人側の回線。制御権を移しても飛び続けるなら、コマンド同期の経路計算の不一致 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:任された人は問題ないと感じるため、報告は「モンスターがおかしい」という形でしか来ません。1人を除く全員が同じモンスターをおかしく見ているなら、まずそのモンスターの制御権を誰が持っているかを確認します。 - 出典: - [Authority (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/authority.html) · Unity · 分散権限モデルでは、ゲームインスタンス(クライアント)ごとに一部のネットワークオブジェクトの権限を持ち、そのオブジェクトを計算 - [Distributed authority topologies (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/distributed-authority.html) · Unity · 権限がクライアントに分散すると単一のシミュレーションがなくなり、チートに弱くなる #### pt-heavy-char · 特定キャラクターのデータ肥大化 · One character with oversized data (inventory, mail, buffs) アイテム・郵便が数千個たまっていたり、フレンド・ブロックリストやバフが極端に多かったりするキャラクターは、接続時・保存時・周囲への通知で扱う量がほかの人の数倍になります。回線に関係なく、そのキャラクターでだけ重くなります。 - なぜ → すると → 画面では:長く育てたキャラクターやイベント報酬で、インベントリ・郵便受けに数千個たまる → 接続・エリア移動・保存のたびにその分DBを読み書きし、周囲に送る装備・バフの情報も大きい → そのキャラクターだけ入場時のロードが長く、インベントリ・郵便を開くときに一瞬止まる。保存をゲームスレッドで待つサーバーなら、周囲の人まで一時的に止まる - 症状:接続不可・無限ロード, 入力遅延, フリーズ / 要因:ストール - 誰に:自分だけ, 特定の機能だけ / いつ:接続直後・メンテ明け, 特定の操作をしたとき, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・DBインフラ - ゲーム開発チームの対応:インベントリ・郵便の保管上限と古い郵便の自動整理、必要な部分だけを分けて読み込む、保存は変わった部分だけをゲームスレッドの外で。 - インフラチームの対応:スロークエリログから同じキャラクターで繰り返される遅いクエリを見つけてゲーム開発チームに伝える、アイテム・郵便の行数が多いキャラクターの上位リストを提供。 - 数値の目安:アイテム1個がDBの1行なら、アイテム5,000個のキャラクターは接続のたびに5,000行を読みます。ふつうのキャラクターの数十倍です。 - グラフでは:一部だけ高い(キャラクターごとの接続・保存時間、キャラクターごとのDB読み取り行数) - 確認箇所:DBのスロークエリログ(MySQL slow query log、PostgreSQL log_min_duration_statement)で同じキャラクターIDで繰り返される遅い読み取り・保存を探し、アイテム・郵便テーブルのキャラクターごとの行数の上位リストを抽出 - 該当する場合:スロークエリが一部のキャラクターIDに集中し、そのキャラクターのアイテム・郵便の行数が平均の数十倍で、別のPC・回線から接続しても同じように遅い - 該当しない場合:同じアカウントの別キャラクターやほかのユーザーも一緒に遅いなら、DBサーバー・ロック側。そのキャラクターが別のPCでは問題ないなら、ユーザーの環境 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:同じキャラクターで別のPC・回線から接続しても同じように重く、同じアカウントの別キャラクターは問題ないなら、キャラクターデータを疑います。報告にキャラクター名が欠かせない理由です。 - 出典: - [Extraneous Fetching antipattern](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/extraneous-fetching/) · Microsoft Azure · 必要以上のデータを取得するとI/O負荷が増え、応答が遅くなる - [PostgreSQL Documentation: Error Reporting and Logging](https://www.postgresql.org/docs/current/runtime-config-logging.html) · PostgreSQL · log_min_duration_statement:一定時間以上かかったSQLを記録し、スロークエリを追跡 - [The Slow Query Log](https://dev.mysql.com/doc/refman/8.4/en/slow-query-log.html) · MySQL · long_query_timeを超えたクエリを記録するスロークエリログ #### pt-phase · チャンネル・インスタンス・フェーズの違い · Different channel / instance / phase 2つのキャラクターが別のチャンネルやインスタンスにいたり、クエストの進行度によって見えるNPCが変わる別の「フェーズ」にいたりすると、互いに違う世界を見ることになります。 - なぜ → すると → 画面では:2つ目のキャラクターが別のチャンネルに割り当てられるか、クエストの段階が違う → サーバーがそのキャラクターにはそのNPCを送らない(正常) → 片方にだけNPCがいない。バグのように見えるが設計どおり - 症状:表示されない・ゴースト / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ, 自分だけ / いつ:常に, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:チャンネル・フェーズの情報をクライアントに送る、QAチェックリストに「2つのキャラクターのチャンネル・クエスト段階を確認」を追加。クライアント:チャンネル・フェーズを画面に表示。 - グラフでは:一部だけ高い(クライアントごとの周囲のオブジェクト数、チャンネル・フェーズ) - 確認箇所:2つのキャラクターのチャンネル番号とそのクエストの進行段階をゲーム画面で比較し、同じチャンネル・段階にそろえて再確認。サーバーのオブジェクト送信ログがあれば、そのNPCをそのキャラクターに送らなかった理由(チャンネル・フェーズ)を確認 - 該当する場合:2つのキャラクターのチャンネルかクエスト段階が違い、同じにそろえるとNPCが見える - 該当しない場合:チャンネル・段階が同じなのに片方にだけいないなら、ロード中に届いた出現通知の破棄、入場直後に集中する出現情報の欠落、視界登録の順序の乱れ側 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:クエストの進行をアカウント単位で保存しているか、キャラクター単位で保存しているかも確認します。同じアカウントの2つのキャラクターなら、片方の進行がもう片方のフェーズを変えることがあります。 - 出典: - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · サーバーは接続ごとに関連する(relevant)アクターだけをレプリケートし、関連しないアクターは送らない - [Object visibility (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/basics/object-visibility.html) · Unity · CheckObjectVisibilityでクライアントごとに見えるオブジェクトを決め、隠したオブジェクトはそのクライアントに送信しない #### pt-loading-drop · ロード中に届いた出現通知の破棄 · Spawn messages dropped before the client is ready ゾーンに入るとすぐにサーバーが周囲のNPCの出現通知を送りますが、クライアントがまだマップを読み込んでいる最中なので、その通知を破棄してしまいます。 - なぜ → すると → 画面では:サーバーが入場処理の直後に周囲のオブジェクトの出現通知を送信 → クライアントはロード中でメッセージハンドラーがまだないため、通知を破棄 → サーバーは送信済みとして扱い、再送しない。視界から外れて戻ってくるまでNPCが表示されない - 症状:表示されない・ゴースト / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ, 自分だけ / いつ:接続直後・メンテ明け, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:ロードが終わったら「準備完了」を送る、またはロード中に受け取ったパケットを保管しておいて処理。サーバー:「準備完了」を受け取ってから周囲の情報を送信。 - 数値の目安:同じPCで2つのクライアントが同時にロードしたり、ロードする側がバックグラウンドウィンドウだったりすると、CPU・ディスクを分け合ううえに処理も制限されるため、そちらのロードが数倍長くなることがあります。サーバーの入場処理が速くなっても、同じバグが表に出ます。 - グラフでは:一部だけ高い(クライアントごとのロード時間、ロード中に破棄したメッセージ数) - 確認箇所:クライアントがロード中に受け取って破棄したメッセージの数・種類とロード完了時刻を、サーバーが出現通知を送った時刻と比較。同じPCで2つのクライアントを同時にロードするか、ロードする側をバックグラウンドウィンドウにすると再現しやすい - 該当する場合:表示されないNPCの出現通知をサーバーは送っており、到着時刻がロード完了前で、その時間に破棄したメッセージ数が増えている。ロードが長い側のクライアントでだけ起きる - 該当しない場合:出現通知がロード完了後に届いているのに表示されないなら、基準スナップショットの欠落かオブジェクトIDの再利用による混同。サーバーがそのNPCの通知をそもそも送っていないなら、視界登録の順序の乱れか、チャンネル・フェーズの違い - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [NetworkConfig class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkConfig.html) · Unity · SpawnTimeout:まだ生成されていないオブジェクトのメッセージは保留し、時間内に生成されなければ破棄 - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · 関連性がなくなったアクターはクライアントから削除され、再び関連すると新たにレプリケート #### pt-aoi-race · 視界登録の順序の乱れ · Interest-management race on enter/leave キャラクターが視界グリッドに登録される瞬間と、NPCがグリッドのセルを移る瞬間が重なると、そのNPCの出現通知が漏れることがあります。 - なぜ → すると → 画面では:入場・チャンネル移動・テレポートの処理とNPCの移動が同じ瞬間に重なる → 「新たに見えるようになったオブジェクト」の計算からそのNPCが漏れる → 特定のNPC数体だけが表示されないか、すでに去ったNPCが残っている - 症状:表示されない・ゴースト / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ, 自分だけ / いつ:移動中・マップ切り替え時, ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:視界の更新を1つのスレッド・1つの順序で処理、定期的に「見えているリスト」全体を合わせ直す。 - グラフでは:不定期なスパイク(サーバーの見えているリストとクライアントのオブジェクトリストの差分数) - 確認箇所:サーバーに、視界グリッドへの登録、オブジェクトのセル移動、出現・消滅通知の送信をティック番号とともに記録し、定期的にサーバーの「見えているリスト」とクライアントが持つリストを比較 - 該当する場合:漏れたNPCが、そのキャラクターの入場・テレポート処理と同じティックにセルを移っており、そのNPCへの出現通知の送信記録がない - 該当しない場合:出現通知は送ったのにクライアントが受け取れなかったか破棄したなら、伝達側(入場直後に集中する出現情報の欠落、ロード中に届いた出現通知の破棄)。いつも同じNPCだけが漏れるなら、フェーズか表示オプションの違い - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Replication Graph in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/replication-graph-in-unreal-engine) · Epic Games · MMORPGなどはワールドをグリッドに分けてセルごとにアクターのリストを持ち、クライアントがいるセルを基準に送信 - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · 関連性は接続ごとに判断し、関連しなくなったアクターはクライアントから削除される #### pt-baseline · 基準スナップショットの欠落 · Lost baseline for delta compression サーバーが「前回から変わったものだけ」を送る方式では、最初に1回送る全体の情報(基準)を失うと、その後の差分を適用できません。 - なぜ → すると → 画面では:オブジェクトの全体情報(基準)のパケットが失われるか、処理前に破棄される → クライアントはその後の差分を適用する対象がないため無視 → そのオブジェクトが表示されないか、かなり後で突然現れる - 症状:表示されない・ゴースト, ワープ / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ, 自分だけ / いつ:ときどきランダムに - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:基準は受信確認(ACK)を受け取るまで必ず再送、差分はクライアントが受信を確認した基準に対してだけ作る。クライアント:基準は実際に適用してから受信確認(ACK)を送る、知らないオブジェクトの差分を受け取ったらサーバーに再要求。 - グラフでは:不定期なスパイク(知らないオブジェクトの差分の受信数) - 確認箇所:クライアントが基準なしに受け取った差分を破棄した回数とオブジェクトIDを、サーバーがそのオブジェクトの基準を送った時刻・ACKを受け取った時刻と照合。開発環境でパケットロスを加えて(tc netemのloss、Unrealのネットワークエミュレーションのパケットロス率)再現 - 該当する場合:表示されないオブジェクトについて、サーバーは基準を送ったもののACKを受け取れていないのに差分だけを送り続け、クライアントはその差分を破棄している - 該当しない場合:基準がACKまで受け取られてクライアントに適用されているのに表示されないなら、消滅通知の欠落かオブジェクトIDの再利用による混同 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Snapshot Compression](https://gafferongames.com/post/snapshot_compression/) · Gaffer On Games · 差分は相手が受信を確認(ack)した基準(baseline)に対してだけ作る必要があり、初期状態は別に送る - [Quake III Arena source: code/server/sv_snapshot.c](https://raw.githubusercontent.com/id-Software/Quake-III-Arena/master/code/server/sv_snapshot.c) · id Software · クライアントが確認したスナップショットを基準にデルタ圧縮し、基準が古すぎればフルスナップショットを送る - [tc-netem(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-netem.8.html) · iproute2 · 送信パケットに遅延・ジッター(delay TIME JITTER)とパケットロス(loss random PERCENT)を加え、実際のネットワークを模倣する試験ツール - [Using Network Emulation in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-network-emulation-in-unreal-engine) · Epic Games · サーバー・クライアントに最小・最大の遅延とパケットロス率を設定して試験、コンソールではNetEmulation.PktLagのように設定 #### pt-ghost · 消滅通知の欠落(ゴースト) · Missed despawn (ghost entity) 逆に「消えた」という通知を取りこぼすと、すでに死んだか去ったNPC・プレイヤーが自分の画面にだけ残ります。 - なぜ → すると → 画面では:死亡・退出・視界外への移動の通知が失われるか、順序が入れ替わる → クライアントはそのオブジェクトがまだいると判断 → 叩いても反応しないモンスター、すでに抜けたプレイヤーが立っている - 症状:表示されない・ゴースト / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ, 自分だけ / いつ:ときどきランダムに, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:定期的に「今見えているリスト」を送る。クライアント:リストにないオブジェクトは削除、動くはずのオブジェクトの更新が長い間なければ非表示にする。 - グラフでは:不定期なスパイク(クライアントにだけ残ったオブジェクト数) - 確認箇所:サーバーが送る「今見えているリスト」とクライアントが持つオブジェクトリストを比較してクライアントにだけあるオブジェクトを数え、消滅通知の送信・受信ログをオブジェクトIDで照合 - 該当する場合:ゴーストの消滅通知をサーバーは送ったのにクライアントに受信記録がないか、消滅が出現より先に届いて順序が逆転している - 該当しない場合:サーバーの見えているリストにもそのオブジェクトが残っているなら、サーバー側のオブジェクト整理漏れ。同じIDで新しいオブジェクトが現れた直後なら、オブジェクトIDの再利用による混同 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · 関連性がなくなった動的アクターはクライアントから削除される - [Object visibility (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/basics/object-visibility.html) · Unity · オブジェクトを隠すと、そのクライアントはオブジェクトをdespawn・削除 #### pt-spawn-burst · 入場直後に集中する出現情報の欠落 · Initial spawn burst lost (unreliable channel, receive buffer, fragmentation) ゾーンに入った瞬間、サーバーは周囲の数十〜数百個のオブジェクトの出現情報を一気に送ります。これを非信頼(unreliable)チャネルで送ったり、ロード中でソケットを読めない間に受信バッファがあふれたりすると、一部が消えて二度と届きません。 - なぜ → すると → 画面では:入場直後に出現情報が短時間に集中して届く → ロード中のクライアントがソケットを読むのが遅れてOSの受信バッファがあふれるか、大きなUDPパケットがフラグメント化され、フラグメントを1つ失っただけで丸ごと消える。非信頼チャネルなら再送もされない → ロードが遅い側のクライアントでだけNPCが数体抜ける。視界から外れて戻ると見える - 症状:表示されない・ゴースト / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ, 自分だけ / いつ:接続直後・メンテ明け, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:出現・消滅通知は必ず再送が保証される信頼チャネルで送る、初期情報は分割して送る。クライアント:受信はロードとは別のスレッドで行う、受信バッファのサイズを増やす。 - 数値の目安:PCのUDP受信バッファのデフォルト値はOSによって違いますが、たいてい数十〜数百KBです。人の多い町の入場情報がこれより大きいと、ロードのせいでソケットを少し読めないだけであふれます。 - グラフでは:接続直後・メンテ明けに急増(入場直後の受信量、出現通知の欠落数) - 確認箇所:サーバーが入場直後に送った出現通知の数とクライアントが受け取った数を比較し、どのチャネル(信頼・非信頼)で送ったかを確認。サーバー側のパケットキャプチャで、入場直後にそのユーザーに送った量とフラグメント化されたパケット(Wiresharkのフィルター:ip.flags.mf == 1 || ip.frag_offset > 0)を確認 - 該当する場合:受け取った数が送った数より少なく、抜けたものが入場直後の集中区間に固まっており、非信頼チャネルで送ったか、大きなパケットがフラグメント化されている。ロードが遅い側のクライアントでより頻繁に起きる - 該当しない場合:送った数と受け取った数が同じなのに表示されないなら、受け取った後に破棄した(ロード中に届いた出現通知の破棄)か、視界計算の問題。入場直後に関係なく随時抜けるなら、回線のパケットロス - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · フラグメント化されたパケットは、フラグメントを1つ失っただけで丸ごと消える - [UDP vs. TCP](https://gafferongames.com/post/udp_vs_tcp/) · Gaffer On Games · UDPは到達・順序を保証しないため、失ったパケットは自分で検知して再送する必要がある - [Socket.ReceiveBufferSize Property](https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receivebuffersize) · Microsoft · ソケットの受信バッファのデフォルトサイズはOSによって違う - [Display Filter Reference: Internet Protocol Version 4](https://www.wireshark.org/docs/dfref/i/ip.html) · Wireshark · ip.flags.mf(More fragments)・ip.frag_offset(Fragment Offset)でフラグメント化されたIPパケットを絞り込む #### pt-id-reuse · オブジェクトIDの再利用による混同 · Entity ID reused without a generation counter 倒されたNPCが再び出現するときにサーバーが同じオブジェクトIDを使い回すと、その間に消滅通知を取りこぼしたクライアントは、新しいNPCを古いNPCと誤認します。 - なぜ → すると → 画面では:NPCが倒され、同じオブジェクトIDで再出現 → 消滅通知を取りこぼしたクライアントは「すでに知っているオブジェクト」として出現通知を無視するか、死亡状態のまま残す → 片方の画面にだけNPCがいないか倒れたまま見える、別のNPCの姿で見えることもある - 症状:表示されない・ゴースト / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ, 自分だけ / いつ:ときどきランダムに, 人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:オブジェクトIDに世代番号を付けて再利用を区別。クライアント:すでに知っているIDの出現通知を受け取ったら、既存のオブジェクトを削除して新たに作る。 - グラフでは:不定期なスパイク(すでに知っているIDの出現通知の数) - 確認箇所:サーバーに、オブジェクトIDごとの生成・削除時刻(世代番号があれば一緒に)を記録し、クライアントがすでに知っているIDで出現通知を受け取った回数と、視界更新時に「変化なし」として処理された削除・再生成を数える - 該当する場合:表示されないか倒れたまま見えるNPCのIDが直前に倒されたNPCと同じで、その間にそのクライアントが消滅通知を受け取れていないか、サーバーが消滅・出現の通知をどちらも送っていない - 該当しない場合:IDに世代番号があり、比較にも使っているなら、この原因ではない。再利用されたIDでないのに表示されないなら、出現通知の欠落側 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - もっと詳しく:サーバー側でも起きます。視界内のオブジェクトリストをIDだけで比較すると、2回の視界更新の間に倒されて同じIDで再スポーンしたNPCを「変化なし」とみなし、消滅・出現の通知をどちらも送りません。視界更新のタイミングが人ごとにずれていると、その瞬間に当たったクライアントだけがこの問題に遭います。 - 出典: - [Entity struct (Entities 1.3)](https://docs.unity3d.com/Packages/com.unity.entities@1.3/api/Unity.Entities.Entity.html) · Unity · EntityはIndexと世代番号(Version)で構成され、再利用されたIndexがまだ有効かを区別 - [NetworkConfig class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkConfig.html) · Unity · RecycleNetworkIds・NetworkIdRecycleDelay:ネットワークIDを一定時間空けてから再利用 #### pt-port-collision · 固定UDPポートの衝突 · Two clients bound to the same local UDP port クライアントが決まったローカルポートを使うように作られていると、同じPCの2つ目のクライアントはそのポートを使えないか、1つ目とパケットを分け合って受け取ることになります。 - なぜ → すると → 画面では:2つのクライアントが同じローカルUDPポートを開こうとする(再利用オプションで無理に共有) → OSが届いたパケットを片方のソケットにだけ渡すか、どちらが受け取るかを保証しない。ルーターとサーバーも2つのクライアントを同じアドレスとみなす → 片方はワールドのパケットを受け取れず、NPC・ほかのプレイヤーが表示されないか切断される - 症状:表示されない・ゴースト, 切断, 接続不可・無限ロード / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ / いつ:接続直後・メンテ明け, 常に - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:ローカルポートはOSに自動で選ばせる(0番をバインド)。サーバー:接続ごとに発行したセッショントークンで接続を区別。 - グラフでは:一部だけ高い(クライアントごとの受信パケット数) - 確認箇所:ユーザーのPCで2つのクライアントを起動したまま、コマンドプロンプトでnetstat -ano -p udpを実行し、ゲームプロセス(PID)ごとに開いているローカルUDPポートを確認。サーバー側では、2つのセッションが同じグローバルIP・同じポートから来ているかを確認 - 該当する場合:2つのゲームプロセスが同じローカルポートにバインドされているか、サーバーから2つのセッションが同じIP・ポートに見える。片方だけ起動すれば正常 - 該当しない場合:2つのクライアントが別々のローカルポートを使っているのに片方がおかしいなら、IP・端末基準のセッション識別バグか、多重起動の制限 - 確認手段:ユーザー側の環境で確認 - 出典: - [Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE](https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse) · Microsoft · SO_REUSEADDRで同じポートに2つ目のbindをするとポートを横取りし、どのソケットがパケットを受け取るかわからない - [bind function (winsock.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind) · Microsoft · ポートを0でbindすると、動的ポート範囲(49152〜65535)から一意のポートを割り当てる - [netstat](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netstat) · Microsoft · -aはTCP・UDPポート、-nは数値アドレス、-oはプロセスID(PID)、-p udpはUDPだけを表示 #### pt-session-key · IP・端末基準のセッション識別バグ · Session keyed by IP or machine ID サーバーや中継サーバーが接続をIPや端末IDで区別していると、同じPC(同じグローバルIP)の2つのクライアントを1人として認識します。 - なぜ → すると → 画面では:セッションテーブルをIP、またはIP+端末IDで作っている → 2つ目のクライアントの情報が1つ目のセッションに上書きされるか混ざる → 片方はNPCが表示されず、もう片方は切断されるかほかの人の情報を受け取る - 症状:表示されない・ゴースト, 切断 / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ, 同じ家, 特定の地域・ISP / いつ:接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:サーバー・中継サーバーとも接続ごとに一意のセッショントークンで区別、同じ家(ルーターのNATの内側)の複数人や、通信事業者が1つのIPを複数の契約者に割り当てるモバイル回線(CGNAT)のユーザーも同じ問題に遭うので必ず修正。クライアント:起動したクライアントごとに個別に受け取ったセッショントークンを使う。 - グラフでは:一部だけ高い(同じグローバルIPの同時セッション数、セッション上書きの数) - 確認箇所:サーバー・中継サーバーのログに、セッション検索に使ったキー、セッショントークン、クライアントのIP・ポートを記録し、同じIPから2つ目の接続が来た瞬間に既存のセッションが変わったかを確認。同じPCで2つのクライアントを順に起動すると再現する - 該当する場合:2つ目のクライアントが接続した瞬間に1つ目のセッションのアドレスやキャラクター情報が変わり、同じルーターやモバイル回線(CGNAT)の内側にいるほかのユーザーでも同じ切断が見られる - 該当しない場合:同じIPの2つのセッションが別々のトークンで個別に維持されているなら、この原因ではない。2つのプロセスが同じローカルポートを使っているなら、固定UDPポートの衝突 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · NAT・CGNで複数の契約者が1つのIPv4アドレスを共有すると、IPだけではユーザーを区別できない #### pt-multiclient · 多重起動の制限 · Multi-client restriction policy セキュリティモジュールやサーバーのポリシーが1台のPCでの複数クライアントを制限していると、2つ目のクライアントは起動・接続ができないか、先に起動した側が切断されます。一部のゲームは追加のクライアントの機能だけを制限します。 - なぜ → すると → 画面では:セキュリティモジュールが多重起動を検知、またはサーバーが同じ端末からの追加接続を制限 → 2つ目の起動・接続を拒否するか、片方を切断。まれに追加のクライアントの一部の機能だけを遮断 → 接続不可か片方の切断。機能だけを制限するゲームでは、片方だけNPC・ショップが表示されない - 症状:接続不可・無限ロード, 表示されない・ゴースト, 切断 / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ / いつ:接続直後・メンテ明け - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:制限するなら明確な案内メッセージを出す、セキュリティモジュールにQA用の例外を設定。サーバー:同じ端末からの接続制限にもQA用の例外を設定。 - グラフでは:一部だけ高い(理由別の接続拒否・切断数(多重接続)) - 確認箇所:2つ目のクライアントを起動したときに出るメッセージと、先に起動した側の切断メッセージを確認。サーバーの接続拒否・強制切断のログに、多重接続・同一端末などの理由コードが残っているかを確認 - 該当する場合:2つ目の起動・接続の瞬間に拒否メッセージが出るか、先に起動した側が多重接続を理由に切断され、クライアントを1つだけ起動すれば問題ない - 該当しない場合:拒否・切断の理由なしに両方とも接続できているのに片方だけNPCが表示されないなら、固定UDPポートの衝突、IP・端末基準のセッション識別バグ、ロード・表示側の原因 - 確認手段:ユーザー側の環境で確認 - 出典: - [CreateMutexW function (synchapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexw) · Microsoft · 名前付きミューテックスがすでに存在するとERROR_ALREADY_EXISTSを返すため、多重起動の検知・単一起動の制限に使われる #### pt-background · バックグラウンドウィンドウの処理制限 · Background window throttling バックグラウンドウィンドウのクライアントでは、ゲーム・エンジン・OSがフレームと処理を減らします。受け取ったパケットを時間内に処理できず、詰まったりあふれたりします。 - なぜ → すると → 画面では:ゲームのオプションやグラフィックスドライバーのバックグラウンドのフレーム制限(例:NVIDIAドライバーでは毎秒20〜200の間で指定)、省電力、エンジンのバックグラウンド停止設定。OSも前面のウィンドウ(フォアグラウンド)にCPU・GPUを優先的に割り当てる → フレームごとに処理するパケット数が減ってキューがたまり、受信バッファがあふれると破棄される → ウィンドウを前面に出すとまとめて現れるか、一部のNPCが最後まで表示されない - 症状:表示されない・ゴースト, 早送り, 切断 / 要因:ストール, パケットロス - 誰に:同じPCの片方のクライアントだけ / いつ:しばらく放置した後, 常に - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:外部・外部 - ゲーム開発チームの対応:ネットワーク受信はゲームループとは別のスレッドで継続、バックグラウンドでも最低限の処理量を保証、エンジンのバックグラウンド実行設定(UnityではrunInBackground)を有効にする。 - 外部の対応:ユーザーに、グラフィックスドライバーのバックグラウンドのフレーム制限とPCの省電力モードを切るよう案内。 - 数値の目安:Unityは、runInBackgroundの設定が無効だと、ウィンドウがフォーカスを失った瞬間にゲームループが止まります。受信をそのループの中だけで行っていると、その間パケットがまったく処理されません。 - グラフでは:途切れた後にまとめて到着(クライアントのフレーム間隔、フレームあたりの処理パケット数) - 確認箇所:同じPCで片方のウィンドウを前面、もう片方を背面に置き、役割を入れ替えながら比較。PresentMonで2つのプロセスのフレーム間隔を測り、ゲーム側のログがあれば、ウィンドウのフォーカス状態とフレームごとに処理したパケット数を確認 - 該当する場合:バックグラウンドウィンドウのときだけフレーム間隔が大きく延びるか(ドライバーの制限なら、設定したフレームレートに対応する間隔で頭打ちになる)処理が止まり、ウィンドウを切り替えると問題もほかのクライアントへ移る - 該当しない場合:前面のウィンドウでも同じように起きるなら、バックグラウンドの制限のせいではない。ウィンドウの位置に関係なくいつも同じクライアントだけがおかしいなら、表示オプションかバージョンの違い - 確認手段:ユーザー側の環境で確認 - 出典: - [Application.runInBackground](https://docs.unity3d.com/ScriptReference/Application-runInBackground.html) · Unity · runInBackgroundのデフォルトはfalseで、このときアプリはバックグラウンドで一時停止 - [Manage 3D Settings (reference) — NVIDIA Control Panel Help](https://www.nvidia.com/content/Control-Panel-Help/vLatest/en-us/mergedProjects/nv3d/Manage_3D_Settings_(reference).htm) · NVIDIA · Background Application Max Frame Rate:バックグラウンドのゲームの最大フレームレートを毎秒20〜200に制限 - [Priority Boosts](https://learn.microsoft.com/en-us/windows/win32/procthread/priority-boosts) · Microsoft · Windowsはフォアグラウンドウィンドウのプロセスの優先度を、バックグラウンドのプロセス以上に引き上げる - [PresentMon README](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README.md) · Intel · WindowsのグラフィックスアプリのCPU・GPU・ディスプレイのフレーム時間をアプリごとに収集するツール #### pt-asset-lock · キャッシュ・アセットファイルへの同時アクセスの競合 · Shared cache / asset file lock conflicts 2つのクライアントが同じキャッシュフォルダーに同時に書き込んだりファイルをロックしたりすると、片方がNPCのモデル・テクスチャを読み込めなくなります。 - なぜ → すると → 画面では:2つのクライアントが同じインストールフォルダーのキャッシュ・アップデートファイルに同時に書き込む → ファイルロックの失敗や、書きかけのファイルを読んでロードに失敗 → ネームプレートはあるのにキャラクターモデルがない、または透明なNPC - 症状:表示されない・ゴースト / 要因:ストール - 誰に:同じPCの片方のクライアントだけ / いつ:接続直後・メンテ明け, 移動中・マップ切り替え時 - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:クライアントごとのキャッシュフォルダー、ファイルロック失敗時の再試行、ロード失敗時はデフォルトのモデルだけでも表示。 - グラフでは:一部だけ高い(クライアントごとのアセットのロード失敗数) - 確認箇所:ユーザーのPCでProcess Monitorを使ってゲームのインストール・キャッシュフォルダーのパスだけに絞り、2つのゲームプロセスのファイルのオープン・書き込みの結果を確認。クライアントログがあれば、アセットのロード失敗とファイルオープンのエラーコード(ERROR_SHARING_VIOLATION)を探す - 該当する場合:表示されないモデルのファイルオープンが共有違反・ロック失敗で終わっており、同じ時刻にほかのクライアントがそのファイルに書き込んでいた。1つだけ起動するか、インストール・キャッシュフォルダーを分けると消える - 該当しない場合:クライアントを1つだけ起動しても同じモデルが表示されないなら、ファイルの破損かクライアントのバージョン・データの不一致。ファイルは正常に開けたのに描画されないなら、メモリ・VRAM不足 - 確認手段:ユーザー側の環境で確認 - 出典: - [Creating and Opening Files](https://learn.microsoft.com/en-us/windows/win32/fileio/creating-and-opening-files) · Microsoft · 共有モードなしで開いたファイルはほかのプロセスが開けず、ERROR_SHARING_VIOLATIONになる - [Process Monitor](https://learn.microsoft.com/en-us/sysinternals/downloads/procmon) · Microsoft · ファイルシステム・レジストリ・プロセスの動作をリアルタイムで記録し、パスなどあらゆる項目で絞り込める #### pt-vram · メモリ・VRAM不足によるストリーミングの失敗 · Memory / VRAM exhaustion 2つのクライアントがビデオメモリを分け合うと、新たに必要なモデル・テクスチャを載せる場所がなく、一部が描画されません。 - なぜ → すると → 画面では:2つのクライアントがVRAM・RAMを分け合う。OSはバックグラウンドウィンドウのビデオメモリの割り当てを先に減らすこともある → エンジンが新しいモデル・テクスチャを載せられないか、下ろしては載せ直すことを繰り返す → NPCが遅れて現れる、ぼやける、表示されない、カクつき - 症状:表示されない・ゴースト, カクつき / 要因:ストール - 誰に:同じPCの片方のクライアントだけ / いつ:移動中・マップ切り替え時, 人が集中したとき - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:外部・外部 - ゲーム開発チームの対応:メモリバジェットに合わせた品質の自動調整、ロード失敗時は代替モデルを表示。 - 外部の対応:2つのクライアントを同時に起動するユーザーに、グラフィック品質を下げるか低スペックモードを使うよう案内、推奨VRAM・RAMの仕様を案内。 - グラフでは:上限で頭打ち(プロセスごとの専用GPUメモリ使用量) - 確認箇所:ユーザーのPCのタスクマネージャーの「詳細」タブに「専用GPUメモリ」列を追加し、2つのクライアントの使用量の合計をグラフィックスカードのVRAM容量と比較。ゲーム側では、DXGIのQueryVideoMemoryInfoが返すバジェット(Budget)と現在の使用量(CurrentUsage)を記録 - 該当する場合:2つのクライアントの使用量の合計がVRAM容量の付近で頭打ちになり、現在の使用量がバジェットを超えた時刻にモデル・テクスチャのロード失敗が集中。品質を下げるか1つだけ起動すると消える - 該当しない場合:VRAMに余裕があるのに表示されないなら、キャッシュ・アセットファイルへの同時アクセスの競合か、表示オプションの違い - 確認手段:ユーザー側の環境で確認 - 出典: - [Residency (Direct3D 12)](https://learn.microsoft.com/en-us/windows/win32/direct3d12/residency) · Microsoft · ビデオメモリのバジェットはほかのアプリに切り替えると大きく減ることがあり、バジェットを超えると停止するか生成に失敗。フォアグラウンドでなければ予約分も保証されない - [GPUs in the task manager](https://devblogs.microsoft.com/directx/gpus-in-the-task-manager/) · Microsoft · タスクマネージャーの詳細タブに列を追加すると、プロセスごとの専用・共有GPUメモリの使用量を確認できる。専用GPUメモリはグラフィックスカードのVRAM - [DXGI_QUERY_VIDEO_MEMORY_INFO structure (dxgi1_4.h)](https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_4/ns-dxgi1_4-dxgi_query_video_memory_info) · Microsoft · Budget(OSが決めたビデオメモリのバジェット)とCurrentUsage(アプリの現在の使用量)。使用量がバジェットを超えるとカクつきが起きることがある #### pt-display-option · 表示オプションの違い · Different display settings 表示人数の制限、NPCのネームプレート・モデルの非表示、低スペックモードなどのオプションが2つのクライアントで違うと、見えるものが変わります。 - なぜ → すると → 画面では:片方のクライアントだけ「周囲のキャラクター表示数の制限」や低スペックモード → 遠くにいるか優先度の低いNPCを描画しない(正常) → 片方にだけNPCがいない - 症状:表示されない・ゴースト / 要因:ストール - 誰に:同じPCの片方のクライアントだけ, 自分だけ / いつ:人が集中したとき, 常に - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:オプションで非表示にしたオブジェクトだとわかるように表示、設定ファイルをクライアントごとに分けて混ざらないようにする。 - グラフでは:一部だけ高い(クライアントごとの画面に描画したオブジェクト数) - 確認箇所:2つのクライアントの表示人数の制限、ネームプレート・モデルの非表示、低スペックモードの設定を並べて比較し、片方をもう片方とまったく同じにそろえてみる。2つのクライアントが1つの設定ファイルを共有して互いに上書きしていないかも確認 - 該当する場合:設定を同じにそろえると2つの画面が同じになり、表示されなかったNPCは表示制限人数の外にある遠いオブジェクトか、優先度の低いオブジェクトだった - 該当しない場合:設定をまったく同じにそろえても片方にだけいないなら、チャンネル・フェーズの違いか出現通知の欠落側 - 確認手段:ユーザー側の環境で確認 - 出典: - [Changing the Quantity of Characters Displayed On-screen (FINAL FANTASY XIV UI Guide)](https://na.finalfantasyxiv.com/uiguide/faq/faq-other/setting_ch_quantity.html) · Square Enix · 表示制限(Character and Object Quantity)の設定で、画面に描画するキャラクター・オブジェクトの数を調整 #### pt-version · クライアントのバージョン・データの不一致 · Client version / data table mismatch 2つ目のクライアントが別のインストール版だったりアップデートが済んでいなかったりすると、サーバーが送った新しいNPCのIDを知らないため、黙って無視します。 - なぜ → すると → 画面では:別フォルダーのインストール版、またはアップデート中に起動したクライアント → 知らないNPC ID・モデルIDを受け取ると読み飛ばす → 新しく追加されたNPCだけが片方で表示されない - 症状:表示されない・ゴースト / 要因:パケットロス - 誰に:同じPCの片方のクライアントだけ / いつ:接続直後・メンテ明け - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:クライアント:接続時にデータのバージョンを送る、知らないIDを受け取ったらログを残して代替表示。サーバー:接続時にデータのバージョンを確認し、違えば接続拒否・アップデートの案内。 - グラフでは:一部だけ高い(クライアントのバージョンごとの、知らないIDの受信数) - 確認箇所:2つのクライアントの実行ファイルのパスと、画面・ログに出るクライアント・データのバージョンを比較。ゲーム側では、接続時に送ったデータのバージョンと、知らないNPC・モデルIDを受け取って読み飛ばした回数を記録 - 該当する場合:2つのクライアントのバージョンかインストールフォルダーが違い、表示されないNPCが最近のアップデートで追加されたもので、アップデートを終えたインストール版では表示される - 該当しない場合:バージョンとインストールフォルダーが同じなのに片方にだけいないなら、チャンネル・フェーズの違いか、ロード・伝達側の原因 - 確認手段:ユーザー側の環境で確認 - 出典: - [NetworkConfig class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkConfig.html) · Unity · ProtocolVersionが違うと互いに通信せず、ForceSamePrefabsで接続時にプレハブのリストの違いをチェック #### pt-priority · 接続ごとの送信バジェット・優先度 · Per-connection bandwidth budget and priority サーバーが接続ごとに送る量に上限を設けて近いものから送ると、上限が低く設定された側は、遠くにいるNPCを遅れて受け取るか、受け取れません。 - なぜ → すると → 画面では:人の多い場所で、サーバーが接続ごとの送信量の上限内で重要度順に送信 → 帯域幅の推定が低く出た接続(例:バックグラウンドウィンドウで受信確認が遅れている側)は、後ろのほうのオブジェクトを後回しにし続ける → 遠くにいるNPCが片方でだけ遅れて見えるか、表示されない - 症状:表示されない・ゴースト, 入力遅延 / 要因:遅延 - 誰に:同じPCの片方のクライアントだけ, 特定の場所・チャンネル / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:後回しになったオブジェクトの優先度を時間とともに上げる(スタベーション防止)、最低限の更新周期を保証。クライアント:バックグラウンドでも受信確認を遅れずに送り、帯域幅の推定が下がらないようにする。 - グラフでは:人数・負荷に連動して上昇(接続ごとの後回しにしたオブジェクト数、接続ごとの送信量) - 確認箇所:サーバーに、接続ごとのティック別送信バイト数、送信上限(推定帯域幅)、送れずに後回しにしたオブジェクト数、オブジェクトごとの最後の送信からの経過時間を記録。UnrealではNetworking Insightsで接続ごとのパケットサイズと、その中に含まれるレプリケーション対象を確認できる - 該当する場合:表示されないNPCがその接続で長く後回しにされたオブジェクトで、その接続の上限がほかの接続より低く、混雑するほど後回しのオブジェクトが増える - 該当しない場合:後回しのオブジェクトがなく、そのNPCも時間どおりに送っているなら、送信後の段階(受信バッファ、ロード、表示オプション)。すべての接続が上限に張り付いているなら、サーバー全体の送信量・視界設計の問題 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · 帯域幅が飽和すると、優先度(距離・視線・最後のレプリケーションからの経過時間)に従ってレプリケートするアクターを選ぶ。すべてのアクターが毎回レプリケートされるわけではない - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · 優先度の累積:今回のパケットに入らなかったオブジェクトが次のパケットに優先して入り、帯域幅の上限はリアルタイムで調整 - [Networking Insights in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-insights-in-unreal-engine) · Epic Games · 接続ごとにやり取りしたパケットのサイズと、その中に含まれるレプリケーション対象・プロパティを表示 #### pt-clock-hold · 時刻推定の誤差によるオブジェクトの保留 · Clock estimate error holds or discards entities クライアントが推定したサーバー時刻がずれていると、届いたばかりのオブジェクト情報を「まだ未来」として保留したり、「古すぎる」として破棄したりします。 - なぜ → すると → 画面では:片方のクライアントのサーバー時刻の推定が大きくずれる(ロード中の測定、省電力からの復帰) → 補間の基準時刻とオブジェクト情報の時刻が合わない → オブジェクトが遅れて現れるか、止まったまま見える - 症状:表示されない・ゴースト, カクつき / 要因:遅延 - 誰に:同じPCの片方のクライアントだけ / いつ:しばらく放置した後, 接続直後・メンテ明け - 主担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:時刻同期を定期的にやり直し、差が大きければ即座にリセット、ロード中や省電力からの復帰直後に測った値は使わない。 - グラフでは:一部だけ高い(クライアントごとのサーバー時刻の推定誤差) - 確認箇所:クライアントに、推定したサーバー時刻、RTT、時刻同期をやり直した時刻、オブジェクト情報を保留・破棄した回数を記録。ロード直後や省電力からの復帰直後に再現してみる - 該当する場合:問題が起きたクライアントだけ推定誤差がリセット基準(UnityではhardResetThresholdSec、デフォルト0.2秒)を超え、オブジェクト情報を未来として保留したり過去として破棄したりした記録があり、時刻同期をやり直すとすぐ正常になる - 該当しない場合:推定誤差が小さいのに遅れて現れるなら、接続ごとの送信バジェット・優先度か、ロード側の原因 - 確認手段:ゲームサーバー・クライアントのログ・メトリクスが必要 - 出典: - [NetworkTimeSystem class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkTimeSystem.html) · Unity · 時間差がhardResetThresholdSec(デフォルト0.2秒)を超えると強制的に合わせ、ふだんはadjustmentRatioで少しずつ速く・遅く調整 - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · LocalTimeはサーバーより先行し、ServerTimeは遅れる。遅れて届いたメッセージは待ち時間がマイナスになることがある ### TCP再送の根本原因(原因20件) #### rt-wireless · 無線区間のパケットロス · Wi-Fi / cellular link loss Wi-Fiとモバイル回線は、無線区間で何度か再送し、それでも届かなければパケットを破棄します。破棄されたパケットは、TCPがかなり後になってから送り直します。 - なぜ → すると → 画面では:電波が弱いか干渉が強く、無線区間での送信が連続して失敗 → 無線機器の再試行上限(通常は数回〜十数回)を超えるとパケットを破棄 → TCPの再送を待つ間止まり、後続のパケットは受信バッファで待たされた後に早送り - 症状:フリーズ, 早送り, ワープ / 要因:パケットロス, ジッター - 誰に:自分だけ, 同じ家 / いつ:ときどきランダムに, 移動中・マップ切り替え時 - 主担当:外部・外部 / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・サーバー開発, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:TCP_NODELAYを有効にする(Nagleアルゴリズムが有効だと、RACKがロス判定に使う後続パケットがない)、再送で詰まっている間に送る状態更新はためずに最新のものだけ送る(カーネルにたまる量はTCP_NOTSENT_LOWATで制限)。クライアント:TCP_NODELAYを有効にする(自分の入力方向のパケットロスはクライアントOSが回復)、パケットロスが集中したりPingが急上昇したりしたら画面にネットワーク状態を表示。 - インフラチームの対応:RACK-TLPでロス回復を速くする(サーバーは無線区間のパケットロスを防げず、できるのは回復を速くすることまで)、最新Linuxのデフォルト値であるnet.ipv4.tcp_recovery=1(RACK)・net.ipv4.tcp_early_retrans=3(TLP)が変更されていないか確認。 - 外部の対応:ユーザーに、有線接続、5GHz・6GHzの利用、ルーターの設置場所・チャンネルの変更を案内。 - 数値の目安:無線区間のパケットロスが1%なら、ゲームのパケット100個のうち1個が消えます。1秒に10個受信するなら、10秒に1回ほどの割合でカクッと止まります。RACK-TLPがなければ、そのたびにRTO(Ping + 200ms以上)の間止まります。 - グラフでは:一部だけ高い(接続ごとの再送率、接続ごとのRTT(Ping)) - 確認箇所:ユーザーのPCからルーター(ゲートウェイ)のアドレスとゲームサーバーにそれぞれpingを数百回送ってパケットロスと遅延の幅を比較し、有線やモバイルデータに切り替えて再測定。サーバーではss -tiでそのユーザーの接続のretransとrtt(平均/偏差)を確認 - 該当する場合:ルーターまでのpingですでにパケットロスやばらつく遅延が見られ、有線に切り替えると消える。サーバーから見ると、そのユーザーの接続だけretransとRTTの偏差が大きい - 該当しない場合:ルーターまでは問題がなく、その先からパケットロスが始まるなら通信事業者・経路側(「ボトルネックでのキューあふれ」「経路変更・ECMPの不良経路」)。同じ通信事業者の複数のユーザーが同時に悪化するなら、通信事業者の区間から確認 - 確認手段:ユーザー側の環境で確認 - もっと詳しく:無線機器の再試行はジッターを生み(再試行ごとに数ms)、再試行上限を超えた場合だけがパケットロスになります。そのため無線の品質が悪くなるほど、「ジッター → ときどき止まる → 頻繁に止まる」の順に症状が大きくなります。アクセスポイント(AP)の間を移る瞬間(ローミング)には、数十ms〜数秒の間、続けてパケットを失うこともあります。モバイル回線は基地局との区間で多く再送するため、パケットロスよりも数百msの遅延の急上昇として現れることが多くなります。 - 実際の事例:ffxiv-2021 - 出典: - [net/wireless/core.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/wireless/core.c?h=v6.12) · Linux kernel · Linuxの無線スタックのデフォルトの再試行上限:短いフレーム7回、長いフレーム4回(dot11ShortRetryLimit・dot11LongRetryLimit) - [RFC 3481: TCP over Second (2.5G) and Third (3G) Generation Wireless Networks](https://www.rfc-editor.org/rfc/rfc3481) · IETF · モバイル回線はリンク層の再送のおかげでIPレベルのパケットロスは少ないが、その回復がジッターと遅延の急上昇として現れる - [Wi-Fi roaming support in Apple devices](https://support.apple.com/guide/deployment/wi-fi-roaming-support-dep98f116c0f/web) · Apple · APを移るときは新しいAPでの認証が終わるまでデータを送れず、802.1X環境では数秒かかることがある - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACK(時間に基づくロス判定)とTLP(末尾パケットの再送)の定義 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_recoveryのデフォルト値0x1(RACK)、tcp_early_retransのデフォルト値3(TLP有効)、TCP_NOTSENT_LOWAT・tcp_notsent_lowatでまだ送信していないデータ量を制限 - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_NODELAYはNagleアルゴリズムを無効にし、小さなデータもすぐに送る - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · RTOの最小値TCP_RTO_MIN = 200ms - [misc/ss.c](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/misc/ss.c?h=v6.12.0) · iproute2 · ss -tiは、retrans:現在再送中の数/累計再送数と、rtt:RTT/RTTの偏差(rttvar)を表示 #### rt-queue-drop · ボトルネックでのキューあふれ(輻輳によるロス) · Tail drop at a congested bottleneck ルーター、通信事業者どうしの接続区間、データセンターの回線のように、最も細い箇所のキューが満杯になると、新しく届くパケットを破棄します。 - なぜ → すると → 画面では:動画・ダウンロード・他のユーザーのトラフィックでボトルネック区間が満杯 → キューが満杯の間、新しく到着するパケットが続けて破棄される(tail drop)。破棄されなかったパケットも満杯のキューの末尾で待たされる → 複数のパケットが一度に消え、長く止まった後に早送り、夜の時間帯に多い - 症状:フリーズ, 早送り, 引き戻し / 要因:パケットロス, 遅延 - 誰に:同じ家, 特定の地域・ISP, サーバー全体 / いつ:夜のピーク時間帯, 人が集中したとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:外部・外部, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:パケットロスが集中したりPingが急上昇したりしたら画面にネットワーク状態を表示(同じ回線で大容量の転送が行われている可能性を案内)。 - インフラチームの対応:データセンター回線の余裕を確保、自社の回線・スイッチポートのキューでの破棄(output drops)カウンターを確認、通信事業者の区間が詰まっていれば別の回線・ピアリングに迂回。 - 外部の対応:ユーザーにルーターのSQM(fq_codel、CAKE)とECNの利用を案内(キューがあふれる前に速度を落とさせる)、通信事業者にボトルネック区間の増強を依頼。 - 数値の目安:キューがあふれる瞬間には、数十msの間に入ってくるパケットのかなりの部分が一度に消えます。続けて失ったうえに送り直したパケットまで失いやすいため、RTOに至ることが多くなります。 - グラフでは:特定の時間帯だけ高い(再送率、RTT(Ping)) - 確認箇所:サーバーの再送率(nstatを1分間隔で実行して得たTcpRetransSegs ÷ TcpOutSegsの増分)と接続ごとのRTTを地域・通信事業者・時間帯別に分けて確認し、自社の回線・スイッチポートの出力破棄(ifOutDiscards)もあわせて確認。問題の地域に向けて、ピーク時間帯と空いている時間帯にmtrを取って比較 - 該当する場合:夜のピークにだけ再送率が上がり、パケットロスの直前にRTTが先に上がる(キューが埋まっていく様子)。mtrではピーク時間帯にだけ、ある区間から終点までパケットロスと遅延がそろって増える - 該当しない場合:パケットロスの直前にRTTが上がらなければ「ポリサーによる超過分の破棄」。時間帯に関係なく常に同じようにパケットを失うなら「物理エラー」か「経路変更・ECMPの不良経路」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 実際の事例:riot-direct-2015 - 出典: - [RFC 7567: IETF Recommendations Regarding Active Queue Management](https://www.rfc-editor.org/rfc/rfc7567) · IETF · tail dropはキューを長く満杯のままにして遅延を増やし、集中したパケットロスを生むという説明と、AQMの推奨 - [RFC 8290: The Flow Queue CoDel Packet Scheduler and Active Queue Management Algorithm](https://www.rfc-editor.org/rfc/rfc8290) · IETF · fq_codel:フローごとのキューとAQMでキューを短く保ち、バッファブロートを減らす - [RFC 3168: The Addition of Explicit Congestion Notification (ECN) to IP](https://www.rfc-editor.org/rfc/rfc3168) · IETF · ECN:パケットを破棄する代わりに、IPヘッダーの印で輻輳を知らせる - [Smart Queue Management](https://www.bufferbloat.net/projects/cerowrt/wiki/Smart_Queue_Management/) · Bufferbloat.net · SQM:フローごとのスケジューリング、キュー長の管理(AQM)、シェーピングを組み合わせる方式 - [Cake](https://www.bufferbloat.net/projects/codel/wiki/Cake/) · Bufferbloat.net · CAKE:シェーパーとfq_codel系のキュー管理を一つにまとめた、ルーター向けのSQM - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatに表示されるTcpRetransSegs・TcpOutSegs(Tcp項目のRetransSegs・OutSegs) - [nstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/nstat.8.html) · iproute2 · nstatはデフォルトで、前回の実行以降の増分を表示 - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifOutDiscards:エラーがないのに、バッファ領域の確保などの理由で送出せずに破棄したパケット数 - [An Internet-Wide Analysis of Traffic Policing](https://research.google/pubs/an-internet-wide-analysis-of-traffic-policing/) · Google · キューあふれではパケットロスの前に待ち時間とRTTが先に上がり、ポリシングはRTTを増やさずに超過分を破棄するという区別(SIGCOMM 2016) #### rt-burst · 送信バーストによる浅いバッファのあふれ · Sender bursts overflow shallow buffers サーバーがティックごとに数千人分の更新を一瞬でまとめて送ると、スイッチの小さなバッファやクラウドの瞬間的な上限が1ms足らずであふれ、一部が破棄されます。 - なぜ → すると → 画面では:ティックの開始時に、全員に送るパケットを一度に送信 → 複数のサーバーのトラフィックが集まるスイッチポートのバッファ(ポートあたり数百KB〜数MB)やクラウドインスタンスの上限が瞬間的にあふれる(平均利用率は低い) → 複数の人が同時にワープしたり一瞬止まったりする、平均値のメトリクスでは原因が見えない - 症状:ワープ, フリーズ, 早送り / 要因:パケットロス - 誰に:特定の場所・チャンネル, サーバー全体 / いつ:人が集中したとき - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ, インフラチーム・ネットワークインフラ - ゲーム開発チームの対応:1ティック分の送信をティック内に分けて送る(数千の接続がティック開始時に集中するのは、接続ごとのペーシングではうまく解消しない)、サーバーごとにティックの開始時刻を分散、大きなデータを送る接続はSO_MAX_PACING_RATEで速度に上限を設定。 - インフラチームの対応:サーバー機器・OS:サーバー全体の送信速度に上限を設定(サーバーOSのシェーパー、Linuxのtc)、1つの接続がまとめて送る分はペーシング(Linuxのfqキュー、BBR)で平準化。ネットワーク:バッファの大きいスイッチ、スイッチポートの出力破棄カウンターを短い間隔で確認(平均利用率では見えない)。 - 数値の目安:10Gbpsのポートが1msで送れる量は約1.25MBです。複数のサーバーのティックが重なって1つのポートに集中すると、バッファはあっという間に埋まります。 - グラフでは:人数・負荷に連動して上昇(スイッチポートの出力破棄数、再送率) - 確認箇所:サーバーがつながっているスイッチポートとその上位のポートの出力破棄(ifOutDiscards)を数秒間隔で収集し、クラウドならethtool -Sのbw_out_allowance_exceeded・pps_allowance_exceededを確認。同じ時刻の再送をbcc tcpretransで収集して突き合わせる - 該当する場合:分単位の平均利用率は低いのに出力破棄やallowance超過が増え、その量が同時接続数や1か所に集まった人数に連動して大きくなる。再送が特定のユーザーのIPアドレス帯(通信事業者・地域)に偏らず、そのサーバーの複数の接続で同じ瞬間に起きる - 該当しない場合:同じポートでCRC・入力エラーも増えていれば「物理エラー」。受信側サーバーのNICの破棄カウンターやsoftnetのdroppedが増えていれば「受信サーバーのホストでのパケット破棄」 - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:ペーシングは接続ごとに個別に動きます。数千の接続がティック開始時に1〜2個ずつ送ることで生じる集中は接続ごとのペーシングではうまく解消できないため、ゲームサーバーが送るタイミングを自分で分散させる必要があります。逆に1つの接続が大きなデータを送るときは、NICが数十KBのデータをパケットサイズに切り分けて続けて送り出しますが(TSO)、このような集中はペーシングがうまく分散してくれます。 - 出典: - [High-Resolution Measurement of Data Center Microbursts](https://research.facebook.com/publications/high-resolution-measurement-of-data-center-microbursts/) · Meta · データセンターのラックスイッチでのバーストは70%以上が数十µs以内に終わり、分単位の平均利用率とドロップの関係は弱い(IMC 2017) - [tc-fq(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-fq.8.html) · iproute2 · fqキューはソケット(接続)ごとにペーシングし、SO_MAX_PACING_RATEで接続ごとの最大速度を決める - [net/ipv4/tcp_bbr.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_bbr.c?h=v6.12) · Linux kernel · BBRは推定したボトルネック帯域幅をもとにpacing_rateを決めて送る - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · TCPはフローの速度に合わせてTSOフレームのサイズを決める(最大64KB、tcp_min_tso_segs) - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · bw_out_allowance_exceeded・pps_allowance_exceeded:インスタンスの帯域幅・秒間パケット数の上限を超えたため、キューに入れられたか破棄されたパケット数 - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifOutDiscards:エラーがないのに、バッファ領域の確保などの理由で送出せずに破棄したパケット数 - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · 再送ごとに相手のアドレス・ポート・接続状態を1行ずつ表示 #### rt-policer · ポリサーによる超過分の破棄 · Traffic policing 通信事業者の料金プラン、クラウドインスタンスの上限、DDoS対策機器は、決められた速度を超えるパケットをキューに入れずにすぐ破棄することもあります。 - なぜ → すると → 画面では:瞬間的な送信量が許容速度・許容バーストを超える → 超えたパケットをキューに入れずにすぐ破棄(ポリシング) → バーストが大きい瞬間ごとに複数のパケットが消え、止まった後に早送り、平均速度は上限を下回って見える - 症状:フリーズ, 早送り, ワープ / 要因:パケットロス - 誰に:サーバー全体, 特定の地域・ISP, 自分だけ / いつ:人が集中したとき, 夜のピーク時間帯 - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ティックごとにまとめて送る量をティック内に分けて送り、瞬間的な送信量を許容バースト以下に下げる、秒間パケット数の上限に引っかかるなら1ティック分のメッセージを1つのパケットにまとめる。 - インフラチームの対応:ネットワーク:機器のポリサー超過カウンターを確認、ポリサーの代わりにシェーパーを使う、許容バーストを増やす。サーバー機器・OS:クラウドの上限超過メトリクス(AWSならethtool -Sのbw_out_allowance_exceeded・pps_allowance_exceeded)を確認、上位のインスタンスタイプへの変更、サーバーでのペーシング(Linuxのfqキュー)。 - 数値の目安:シェーパー(キューに入れて遅らせる)は遅延を増やし、ポリサー(すぐ破棄)はパケットロスを増やします。TCPのゲーム接続はパケットロス1回で数百ms止まることがあるため、上限を少し超える程度なら、たいていはポリサーのほうが影響が大きくなります。 - グラフでは:上限で頭打ち(短い間隔での送信量、ポリサー・allowance超過カウンター) - 確認箇所:ポリサーが設定された機器の超過(exceed)・破棄カウンターを確認し、クラウドならethtool -Sのbw_out_allowance_exceeded・pps_allowance_exceededを確認。パケットロスが起きた接続は、ss -tiのrttやパケットキャプチャでロス直前のRTTを確認 - 該当する場合:超過カウンターが増え、短い間隔で見た送信量がある値で切り取られたように平らになっている。パケットロスの直前にRTTが上がらず、バーストが大きい瞬間にだけ複数のパケットが一度に消える - 該当しない場合:パケットロスの直前にRTTが先に上がればキューあふれ(「ボトルネックでのキューあふれ」「送信バーストによる浅いバッファのあふれ」)。超過カウンターが変わらなければ別の原因 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 2475: An Architecture for Differentiated Services](https://www.rfc-editor.org/rfc/rfc2475) · IETF · シェーピングはパケットを遅らせてトラフィックプロファイルに合わせ、ポリシングはプロファイルを超えるパケットを破棄するという定義 - [An Internet-Wide Analysis of Traffic Policing](https://research.google/pubs/an-internet-wide-analysis-of-traffic-policing/) · Google · ポリシングがかかった転送はパケットロス率が平均6倍高く、ペーシング・シェーピングで同じ目的を達成できる。ポリシングはRTTを増やさずに超過分を破棄し、キューあふれはパケットロスの前にRTTが上がるという区別(SIGCOMM 2016) - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · ethtool -Sのbw_out_allowance_exceeded・pps_allowance_exceeded:インスタンスの上限を超えたため、キューに入れられたか破棄されたパケット数 - [tc-fq(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-fq.8.html) · iproute2 · Linuxのfqキューによる接続ごとのペーシング - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ss -iのrtt(平均往復時間) #### rt-physical · 物理エラー(不良ケーブル・光モジュール・コネクター) · Bit errors: bad cable, optics, dirty fiber ケーブルの損傷、ほこりの付いた光コネクター、寿命を迎えた光モジュールはビットエラーを起こし、壊れたパケットは機器が黙って破棄します。 - なぜ → すると → 画面では:ケーブル・光モジュール・コネクターの不良でビットが反転 → チェックサム(CRC)が合わないパケットを機器が破棄 → その経路を通る人だけが継続的に一瞬止まっては早送り、時間帯とは無関係 - 症状:フリーズ, 早送り, ワープ / 要因:パケットロス - 誰に:特定の場所・チャンネル, 同じ家 / いつ:常に - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ, 外部・外部 - インフラチームの対応:CRCエラーは壊れた方向の受信側で計上されるため、両端とも確認。ネットワーク:機器ポートのCRC・入力エラーカウンターを確認、光信号の強度を点検(スイッチの光モジュール情報)、光コネクターの清掃、ケーブル・光モジュールの交換。サーバー機器・OS:サーバーのethtool -Sのrx_crc_errors(ドライバーによって名前が少し違う)を確認、光信号の強度を点検(ethtool -m)、サーバー側のケーブル・NICの交換。 - 外部の対応:ユーザーの家の区間ならLANケーブル・ルーターの交換を案内、通信事業者の回線区間なら通信事業者に回線の点検を依頼。 - 数値の目安:パケットロスが0.1%でも、ゲームのパケット1,000個に1回です。その経路を通る人が数十人いれば、数秒ごとに誰かがカクッと止まります。ビットエラーは大きなパケットほど起きやすくなります。 - グラフでは:一部だけ高い(ポートごとのCRCエラー数、サーバー・ポートごとの再送率) - 確認箇所:リンク両端のCRCカウンターを確認。サーバーはethtool -Sのrx_crc_errorsやip -s -s linkのcrc、スイッチはポートのFCSエラー(dot3StatsFCSErrors)・入力エラー(ifInErrors)。光リンクならethtool -mとスイッチの光モジュール情報で受信光強度を確認 - 該当する場合:1つのポートのCRCエラーが時間帯に関係なく継続的に増え、そのポートを通るサーバー・接続だけ再送率が高い。同じ種類の他のリンクより受信光強度が低い - 該当しない場合:CRCは変わらないのに出力破棄だけが増えるならキューあふれ(「送信バーストによる浅いバッファのあふれ」「ボトルネックでのキューあふれ」)。片側のレイトコリジョンともう片側のCRCがそろって増えるなら「デュプレックスの不一致」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · rx_crc_errorsは受信側のインターフェースがCRCエラーとして数えたパケット数、ip -s -s linkとethtool -Sで確認 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · -SでNIC・ドライバーごとの統計、-mで光モジュール(SFP+・QSFP)のEEPROMと光診断情報を確認 - [RFC 3635: Definitions of Managed Objects for the Ethernet-like Interface Types](https://www.rfc-editor.org/rfc/rfc3635) · IETF · スイッチポートのFCSエラー(dot3StatsFCSErrors)、このエラーは入力エラー(ifInErrors)に合算 #### rt-duplex · デュプレックスの不一致 · Duplex mismatch 片側はオートネゴシエーション、もう片側は速度・デュプレックスを固定にしておくと、片側が半二重で動作し、負荷がかかるたびに衝突(コリジョン)でパケットを失います。 - なぜ → すると → 画面では:機器の片側だけ速度・デュプレックスを固定設定 → 片側は全二重、もう片側は半二重で動作し、コリジョン・レイトコリジョンが発生 → 普段は問題ないが、トラフィックが増えるとその機器を通る人全員が止まっては早送り - 症状:フリーズ, 早送り / 要因:パケットロス - 誰に:サーバー全体, 特定の場所・チャンネル / いつ:人が集中したとき, 夜のピーク時間帯 - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ - インフラチームの対応:両側ともオートネゴシエーション、または両側とも同じ値で固定。ネットワーク:スイッチポートの状態で速度・デュプレックスを確認、ポートカウンターで半二重側はレイトコリジョン、全二重側はCRCエラー・短すぎるフレーム(runt)が増えていないか確認。サーバー機器・OS:ethtoolで速度・デュプレックスを確認。 - 数値の目安:1Gbpsの銅線ケーブルはオートネゴシエーションが必須で、10Gbps以上には半二重そのものがありません。そのため最近は主に、100Mbps以下の古い機器、管理用ポート、一部の回線の接続区間で起きます。 - グラフでは:人数・負荷に連動して上昇(ポートのレイトコリジョン・CRCエラー数、再送率) - 確認箇所:リンク両側の実際の速度・デュプレックスを確認。サーバーはインターフェース名だけを付けて実行したethtool、スイッチはポートの状態やSNMPのdot3StatsDuplexStatus。レイトコリジョン(サーバーのtx_window_errors、スイッチのdot3StatsLateCollisions)とCRCエラーもあわせて確認 - 該当する場合:片側は半二重、もう片側は全二重と表示される。トラフィックが増えるたびに、半二重側はレイトコリジョン、全二重側はCRCエラーがそろって増える - 該当しない場合:両側の速度・デュプレックスが同じでCRCだけが増えるなら「物理エラー」。10Gbps以上のリンクには半二重がないため、この原因からは除外 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Linux Base Driver for Intel(R) Ethernet Network Connection](https://docs.kernel.org/networking/device_drivers/ethernet/intel/e1000.html) · Linux kernel · 1000BASE-Tの規格はオートネゴシエーションを要求 - [IEEE 802.3ae 10 Gigabit Ethernet: HSSG Objectives](https://www.ieee802.org/3/ae/objectives.pdf) · IEEE · 10ギガビットイーサネットは全二重のみをサポート - [IEEE P802.3ba Objectives](https://www.ieee802.org/3/ba/PAR/P802.3ba_Objectives_0709.pdf) · IEEE · 40・100ギガビットイーサネットも全二重のみをサポート - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · tx_window_errorsはレイトコリジョン(late collision)で失敗した送信の数、rx_crc_errorsはCRCエラーのあった受信パケット数 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · ethtool -sのspeed・duplex・autonegで速度・デュプレックス・オートネゴシエーションを設定、インターフェース名だけを渡すと現在の設定を表示 - [RFC 3635: Definitions of Managed Objects for the Ethernet-like Interface Types](https://www.rfc-editor.org/rfc/rfc3635) · IETF · dot3StatsDuplexStatus(halfDuplex・fullDuplexで現在のデュプレックスを表示)、dot3StatsLateCollisions(レイトコリジョンの数) #### rt-host-drop · 受信サーバーのホストでのパケット破棄 · Receiver host drops (ring, softirq, CPU) パケットはサーバーまで届いたのに、NICのリングバッファ(到着したパケットを一時的に入れておくバッファ)があふれたり、カーネルで受信処理を担うコアが飽和したりして破棄されます。 - なぜ → すると → 画面では:接続数の急増・1つのコアに集中した割り込み・仮想マシンのCPUスチール・仮想スイッチの過負荷 → リングバッファ(rx_missed_errorsなど、名前はドライバーによって異なる)やカーネルの受信キュー(softnet dropped)で破棄 → 人が集中すると、サーバー全体で同時に入力の反映が遅れ、一瞬止まる - 症状:入力遅延, フリーズ, 早送り, ワープ / 要因:パケットロス, ストール - 誰に:サーバー全体 / いつ:人が集中したとき - 主担当:インフラチーム・サーバーインフラ - インフラチームの対応:ethtool -Sのrx_missed_errorsのような破棄カウンターと/proc/net/softnet_statのdroppedを監視に追加、リングバッファのサイズを増やす(ethtool -G)、RSS・割り込みを複数のコアに分散、ゲームスレッドと受信処理のコアを分離、CPUの余裕を確保、仮想マシンならCPUスチール・仮想スイッチの負荷を確認。 - 数値の目安:サーバーが受信中に破棄したパケットは、クライアントが送り直します。そのためサーバーの再送メトリクスにはほとんど表れず、ethtool -Sのrx_missed_errorsのような破棄カウンター(名前はドライバーによって異なる)と/proc/net/softnet_statのdroppedに先に現れます。 - グラフでは:上限で頭打ち(コアごとのsoftirq使用率、NICの破棄カウンター) - 確認箇所:ethtool -Sの破棄カウンター(rx_missed_errorsなど、mlx5ではrx_out_of_buffer・rx_discards_phy)、ip -s -s linkのmissed、/proc/net/softnet_statの2列目(dropped)・3列目(time_squeeze)を確認し、mpstat -P ALLでコアごとの%soft(ソフト割り込み処理)を確認。仮想マシンなら%stealも確認 - 該当する場合:人が集中する時刻に破棄カウンターやsoftnet droppedが増え、受信処理を担うコアの%softが100%近くで頭打ちになる。そのサーバーのすべての接続で同時に入力が遅れる - 該当しない場合:サーバーの破棄カウンターが変わらず、再送が特定の地域・通信事業者の接続に集中するなら経路上のパケットロス。サーバーが送ったパケットを経路上で失った場合は、サーバーのnstat TcpRetransSegsが増え、これらのカウンターは変わらない - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · rx_missed_errorsはバッファがなくてホストが受け取れなかったパケット数(/proc/net/devではdropに合算)、ip -s -s linkで確認 - [drivers/net/ethernet/intel/igb/igb_ethtool.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/intel/igb/igb_ethtool.c?h=v6.12) · Linux kernel · ethtool -Sのカウンター名はドライバーが決める(例:igbのrx_missed_errors・rx_no_buffer_count) - [Ethtool counters](https://docs.kernel.org/networking/device_drivers/ethernet/mellanox/mlx5/counters.html) · Linux kernel · mlx5ドライバーのrx_out_of_buffer(受信キューにバッファがない)・rx_discards_phy(ポートのバッファ不足で破棄) - [net/core/net-procfs.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/net-procfs.c?h=v6.12) · Linux kernel · /proc/net/softnet_statはCPUごとに1行、16進数で、2列目がdropped、3列目がtime_squeeze - [Documentation for /proc/sys/net/](https://docs.kernel.org/admin-guide/sysctl/net.html) · Linux kernel · netdev_max_backlog:カーネルの処理より速くパケットが入ってくるときにためておく受信キューの上限 - [Scaling in the Linux Networking Stack](https://docs.kernel.org/networking/scaling.html) · Linux kernel · RSS:NICが複数の受信キューに振り分け、複数のCPUで処理 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · -G(--set-ring)でリングバッファのサイズを変更 - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · /proc/statのsteal:仮想化環境で他のOSがCPUを使っていた時間 - [mpstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/mpstat.1.html) · sysstat · mpstat -P ALLでコアごとの使用率、%softはソフト割り込みの処理時間、%stealはハイパーバイザーが別の仮想CPUを処理していたために待たされた時間 - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatに表示されるTcpRetransSegs(Tcp項目のRetransSegs) #### rt-stateful-fw · ファイアウォール・接続追跡による破棄 · Stateful firewall / conntrack drops ファイアウォールやLinuxの接続追跡(conntrack、通過する接続をテーブルに記録する機能)は、テーブルが満杯になったり、接続の状態が合わないと判断したりすると、パケットを破棄します。 - なぜ → すると → 画面では:接続追跡テーブルが満杯(table full)、または行きと帰りの経路が異なり、片方向だけがファイアウォールを通る(非対称経路) → ファイアウォールが「知らない接続」や「ウィンドウの範囲から外れたシーケンス番号」のパケットとみなして破棄 → テーブルが満杯になると新規接続ができなくなり、経路がずれるとその経路の人だけが再送を繰り返した末に切断 - 症状:フリーズ, 切断, 接続不可・無限ロード / 要因:パケットロス - 誰に:サーバー全体, 特定の地域・ISP / いつ:人が集中したとき, 接続直後・メンテ明け, ときどきランダムに - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・サーバー開発, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:テーブルが満杯になる場合に備え、ログイン待機列の仕組みで集中する接続を調整、短い接続を繰り返さないよう接続を再利用(サーバー間の呼び出しを含む)、ハートビートが途絶えた接続は先に整理。クライアント:接続に失敗したり切断されたりしたら、再試行間隔を延ばしながらランダムに分散(テーブルが満杯のときに一斉に再び殺到しないように)。 - インフラチームの対応:ネットワーク:ファイアウォールの接続追跡テーブルのサイズを増やす、ゲームのポートは接続追跡から外す、行きと帰りの経路が同じファイアウォールを通るようルーティングを合わせる、ファイアウォールのTCPウィンドウ検査の設定を確認。サーバー機器・OS:Linuxのテーブルサイズを増やす(nf_conntrack_max)、ゲームのポートは接続追跡から外す(NOTRACK)、TCPウィンドウ検査の設定を確認(nf_conntrack_tcp_be_liberal)、AWSならconntrack_allowance_exceededも確認。 - 数値の目安:Linuxのconntrackの上限(nf_conntrack_max)のデフォルト値は、メモリ量に応じて数万〜数十万個です。現在の数(nf_conntrack_count)が上限に達すると、ログに「nf_conntrack: table full, dropping packet」が記録されます。 - グラフでは:上限で頭打ち(conntrackのエントリ数(nf_conntrack_count)、新規接続の失敗数) - 確認箇所:Linuxサーバーではnf_conntrack_countとnf_conntrack_max、dmesgの「nf_conntrack: table full, dropping packet」、/proc/net/stat/nf_conntrackのdrop・invalid(コアごとに1行、16進数)を確認。ファイアウォールではセッションテーブルの使用量とドロップログ、AWSではethtool -Sのconntrack_allowance_exceededを確認 - 該当する場合:エントリ数が上限で平らになり、同じ時刻にtable fullのログとdrop、またはconntrack_allowance_exceededが増える。非対称経路の場合は上限に余裕があるのに、invalidとファイアウォールのドロップログが特定の経路の接続で増える - 該当しない場合:エントリ数が上限から遠く、invalid・ドロップログも変わらなければ別の原因。テーブルには余裕があるのにファイアウォールのCPUや秒間パケット数が上限に達しているなら「中間機器の処理上限超過」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · nf_conntrack_maxのデフォルト値はハッシュバケット数(メモリ÷16384、1,024〜262,144)、現在の数はnf_conntrack_count、nf_conntrack_tcp_be_liberalはウィンドウ外のRSTだけをINVALIDとして扱う - [net/netfilter/nf_conntrack_core.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_core.c?h=v6.12) · Linux kernel · テーブルが満杯になると「nf_conntrack: table full, dropping packet」をログに記録して破棄(drop統計が増加)、接続状態に合わないパケットはinvalid統計が増加 - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · rawテーブルのCT --notrackで接続追跡から除外 - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · インスタンスごとの追跡接続数の上限を超えるとパケットを破棄し、conntrack_allowance_exceededで確認できる、非対称経路は避けるべきという推奨 - [net/netfilter/nf_conntrack_standalone.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_standalone.c?h=v6.12) · Linux kernel · /proc/net/stat/nf_conntrackはコアごとに1行、16進数で、entries・invalid・insert_failed・drop・early_dropなどの列がある #### rt-appliance-pps · 中間機器の処理上限超過(ファイアウォール・IPS・DDoS対策) · Inline appliance PPS / CPU overload ファイアウォール、侵入防止システム(IPS)、DDoS対策機器は、通過するパケットを1つずつ検査します。検査能力を超えた瞬間から、処理しきれなかったパケットを破棄します。 - なぜ → すると → 画面では:ピーク時間帯・イベントで小さなゲームパケットが毎秒数十万個以上集中、または検査ルールが重い → 機器のCPU・秒間パケット数が上限に達し、機器で破棄。誤検知なら正常なパケットも遮断 → その機器の後ろにあるサーバー全体で同時にフリーズ・ワープ、人が集中したときだけひどくなる - 症状:フリーズ, 早送り, ワープ, 切断 / 要因:パケットロス, 遅延 - 誰に:サーバー全体, 特定の地域・ISP / いつ:夜のピーク時間帯, 人が集中したとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:ゲームのトラフィックパターン(ポート、パケットサイズ、秒間パケット数)をインフラチームに共有、1ティックで送る小さなメッセージはまとめて一度に送り、パケット数を減らす。 - インフラチームの対応:機器のCPU・秒間パケット数・ドロップカウンターをゲームのメトリクスと並べて確認、小さいパケットを基準に機器の容量を見積もる、ゲームのポートは重い検査から外す、DDoS対策のルールをゲームのトラフィックパターンに合わせる。 - 数値の目安:機器仕様の「10Gbps」は、1,500バイトの大きなパケットを基準に書かれていることが多いです。100バイト前後のゲームパケットは同じ帯域幅でもパケット数が10倍以上多くなるため、回線が空いて見えても秒間パケット数の上限が先に埋まります。 - グラフでは:上限で頭打ち(機器の秒間パケット数・CPU使用率、機器のドロップ数) - 確認箇所:機器のCPU・秒間パケット数・ドロップカウンターを確認し、機器の前後にあるスイッチポートのパケット数を同じ間隔で比較。同時接続数やサーバーの再送率と1つの画面に重ねて確認 - 該当する場合:ピーク・イベント時に機器の秒間パケット数やCPUがある値で頭打ちになり、機器に入ったパケットより出たパケットが少なくなる。同じ時刻に、その後ろのサーバー全体の再送率もそろって上がる - 該当しない場合:機器の前後のパケット数が同じで、機器でのドロップもなければ別の原因。サーバーのNICの破棄カウンターやsoftnet droppedが増えていれば「受信サーバーのホストでのパケット破棄」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 2544: Benchmarking Methodology for Network Interconnect Devices](https://www.rfc-editor.org/rfc/rfc2544) · IETF · 機器の性能は、最小・最大サイズを含む複数のフレームサイズで試験すべき(パケットサイズによって処理性能が変わる) #### rt-mtu · MTUブラックホール(大きいパケットだけ繰り返し失われる) · PMTU black hole 途中の区間が受け取れるサイズが小さくなったのに「大きすぎる」という通知(ICMP)が遮断されると、大きなパケットは何度送り直しても消え続けます。 - なぜ → すると → 画面では:VPN・トンネル区間で最大サイズが小さくなり、サイズ超過の通知はファイアウォールで遮断 → 送信側は理由がわからないまま同じ大きなパケットを再送し続け、RTOは2倍ずつ増加 → 普段は問題ないが、インベントリ・人の多い場所・入場時のロードのように大きなデータがやり取りされる瞬間に、後続の小さなパケットまですべて止まり、最終的に切断や無限ロード - 症状:フリーズ, 切断, 接続不可・無限ロード / 要因:パケットロス - 誰に:特定の地域・ISP, 自分だけ / いつ:特定の操作をしたとき, 接続直後・メンテ明け - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:サーバー側で直接下げるならソケットの最大セグメントサイズ(TCP_MAXSEG)を設定、ゲームコードでメッセージを細かく分割するだけでは防げない(TCPが送るデータをMSSのサイズでまとめ直すため)。 - インフラチームの対応:ネットワーク:境界の機器でMSSを調整(clamping)、ファイアウォール・クラウドのネットワークACLでサイズ超過のICMP(タイプ3コード4、fragmentation needed)を許可。サーバー機器・OS:経路MTUの設定、サーバーのファイアウォール・クラウドのセキュリティグループでもサイズ超過のICMPが遮断されていないか確認、最後の安全策としてLinuxのtcp_mtu_probing=1。 - 数値の目安:通常は1,500バイトで、トンネルを通ると1,400前後です。同じパケットが5〜6回再送されると、止まる時間は10秒を超えます。 - グラフでは:一部だけ高い(接続ごとのRTO・backoff、地域・通信事業者ごとの切断数) - 確認箇所:問題の接続の再送をサーバー側のパケットキャプチャやbcc tcpretrans -s(シーケンス番号を表示)で確認し、ss -tiでその接続のmss・pmtu・backoffを確認。サーバーからそのユーザーのアドレスに、小さなpingとDFを立てた1,500バイトのping(ping -M do -s 1472)を送って比較 - 該当する場合:MSSいっぱいのパケットが同じシーケンス番号で、間隔を2倍ずつ延ばしながら再送され続け、それより小さなパケットは届く。サイズ超過のICMP(Wiresharkのフィルターicmp.type == 3 and icmp.code == 4)は届かず、小さなpingには応答があるのに、大きなDF付きのpingだけが応答なく消える - 該当しない場合:小さなパケットも一緒に消えるなら、サイズと関係ないパケットロス(「ボトルネックでのキューあふれ」「経路変更・ECMPの不良経路」)。サイズ超過のICMPが届き、ss -tiのpmtuが小さくなるなら、経路MTU探索は正しく動作している - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:tcp_mtu_probing=1は、再送タイムアウトが数秒(tcp_retries1=3に相当)続いた後になって初めてブラックホールと判断し、MSSを1,024バイトに下げます。その間は止まったままになるため、これは最後の安全策にとどめ、事前に防ぐMSSの調整を先に行います。 - 出典: - [RFC 1191: Path MTU discovery](https://www.rfc-editor.org/rfc/rfc1191) · IETF · 経路MTU探索:大きすぎるパケットはICMP「fragmentation needed and DF set」(タイプ3コード4)で通知 - [RFC 2923: TCP Problems with Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc2923) · IETF · ICMPが遮断され、大きなパケットだけが消え続けるPMTUブラックホールの問題 - [RFC 4821: Packetization Layer Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc4821) · IETF · ICMPを使わずにトランスポート層がパケットサイズを探る方法(Linuxのtcp_mtu_probingの土台) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_mtu_probing:0は無効、1はブラックホールを検知したときだけ、2は常に(開始時のMSSはtcp_base_mss)。tcp_retries1のデフォルト値は3 - [net/ipv4/tcp_timer.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · RTOによる再送がtcp_retries1回続くとブラックホールを検知したとみなし、MTU探索を有効にしてMSSを下げる - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_BASE_MSS = 1,024バイト - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · 再送タイマーが満了するたびにRTOを2倍に延ばす - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · TCPMSS --clamp-mss-to-pmtu:ICMPを遮断する区間のせいで大きなパケットが止まる問題を、SYNのMSSを調整して回避 - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_MAXSEG:送出するパケットの最大セグメントサイズ、接続前に設定すると相手に通知するMSSも変わる - [Network maximum transmission unit (MTU) for your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/network_mtu.html) · AWS · インターネットゲートウェイ・VPNはMTU 1,500、PMTUDにはICMPタイプ3コード4が必要で、セキュリティグループ・ネットワークACLが遮断すると受け取れない - [MTU considerations | Cloud VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/mtu-considerations) · Google Cloud · Cloud VPNゲートウェイのMTUは1,460バイト、IPv4トンネルのペイロードMTUは1,406バイト(トンネルを通ると1,400前後) - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · 再送ごとに1行ずつ表示し、-sは再送したパケットのシーケンス番号もあわせて表示 - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ss -iのmss、pmtu(経路MTU)、backoff(RTOが2倍に延びた回数) - [ping(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ping.8.html) · iputils · -M doはDFを立て、カーネルが把握している経路MTUより大きなパケットは送らない、-sはデータサイズ(ICMPヘッダーの8バイトは別) - [Display Filter Reference: Internet Control Message Protocol](https://www.wireshark.org/docs/dfref/i/icmp.html) · Wireshark · icmp.type・icmp.codeの表示フィルター #### rt-mapping · 接続中のNAT・ロードバランサーのマッピング期限切れ · NAT / load balancer mapping expired mid-connection アイドル接続のマッピング(この接続をどこに転送するかを記録したエントリ)を中間機器が消すと、次に送るパケットは転送されません。再送だけを繰り返した末に切断されるか、機器が接続拒否(RST)を返してすぐに切断されます。 - なぜ → すると → 画面では:しばらくパケットのやり取りがない接続(離席、ロビー) → ルーターのNAT・通信事業者のCGNAT・ファイアウォール・ロードバランサー・クラウドのセキュリティグループがアイドル状態のマッピングを削除 → 再び動いた瞬間に再送が続いた末に切断、またはすぐに切断 - 症状:切断, フリーズ / 要因:パケットロス - 誰に:自分だけ, 特定の地域・ISP / いつ:しばらく放置した後 - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発, インフラチーム・ネットワークインフラ, インフラチーム・サーバーインフラ - ゲーム開発チームの対応:クライアント:最も短いアイドルタイムアウトの半分以下の間隔でハートビートを送る(ユーザーのルーター・通信事業者のCGNATのマッピングは内側から出ていくパケットでしか確実に更新されず、そのタイムアウトは自社では変えられないため、クライアントが送る)、切断されたら自動で再接続。サーバー:ハートビートに応答し、一定時間受信できなければ接続を先に整理(TCP keepaliveの間隔を縮める(TCP_KEEPIDLEなどのソケットオプション)、TCP_USER_TIMEOUTで早く検知)、セッショントークンで引き継ぐ。 - インフラチームの対応:ネットワーク:経路上のファイアウォール・ロードバランサーのアイドルタイムアウトをまとめてゲーム開発チームに共有、自社のファイアウォール・ロードバランサーは必要なら延ばす。サーバー機器・OS:クラウドのセキュリティグループの接続追跡時間を確認してゲーム開発チームに共有。 - 数値の目安:TCPのマッピングを保持する時間は、機器によって数分から数時間までまちまちです。クラウドのセキュリティグループが接続を追跡する設定の場合、AWSのNitro v6インスタンスタイプはデフォルトで350秒後に追跡エントリを削除します(その他のタイプは5日、「クラウドのセキュリティグループによる接続追跡の期限切れ」の項目を参照)。LinuxのTCP keepaliveはデフォルト値が「2時間アイドルなら確認」なので、ほとんどの機器より遅くなります。 - グラフでは:接続が一斉に切れる(切断数、切断前のアイドル時間) - 確認箇所:切断された接続の最後の数分をサーバー側のパケットキャプチャで確認し、生きている接続はss -tiのlastsnd・lastrcv(最後に送信・受信してから経過したms)でアイドル時間を確認。nstatのTcpExtTCPAbortOnTimeout(タイマーが切れて接続をあきらめた数)もあわせて確認 - 該当する場合:切断された接続はどれも、直前のアイドル時間が同じような値(経路上の機器のアイドルタイムアウト、例:AWS Nitro v6インスタンスのセキュリティグループの350秒)を超えており、アイドル後の最初のパケットからACKのない再送だけが続いてあきらめるか、すぐにRSTが返ってくる - 該当しない場合:アイドル時間と関係なくプレイ中にも切断されるなら別の原因(「経路変更・ECMPの不良経路」「ファイアウォール・接続追跡による破棄」)。ハートビートが最も短いアイドルタイムアウトの半分以下の間隔でやり取りされている接続なら、この原因からは除外 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 5382: NAT Behavioral Requirements for TCP](https://www.rfc-editor.org/rfc/rfc5382) · IETF · TCPのNATの接続アイドルタイムアウトは2時間4分以上であるべきという推奨(機器がアイドルセッションを先に消す可能性があることが前提) - [RFC 4787: Network Address Translation (NAT) Behavioral Requirements for Unicast UDP](https://www.rfc-editor.org/rfc/rfc4787) · IETF · NATのマッピングは内側から出ていくパケットで更新されなければならず(REQ-6)、外から入ってくるパケットによる更新は任意(UDPの場合) - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · TCPのアイドル追跡タイムアウトのデフォルト値は、Nitro v6インスタンスタイプで350秒、その他のタイプで432,000秒(5日)。5分より短い間隔のkeepaliveを推奨 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_keepalive_timeのデフォルト値は2時間 - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_KEEPIDLE(keepaliveを始める前のアイドル時間)、TCP_USER_TIMEOUT(確認応答のないデータを待ち続け、接続を閉じるまでの時間) - [RFC 5482: TCP User Timeout Option](https://www.rfc-editor.org/rfc/rfc5482) · IETF · TCPユーザータイムアウト:送ったデータに確認応答がないまま、どれだけ経過したら接続を閉じるか - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ss -iのlastsnd・lastrcv:最後に送信・受信してからの経過時間(ms) - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPAbortOnTimeout:TCPのタイマーが切れ、RSTなしで接続をあきらめた数 #### rt-path · 経路変更・ECMPの不良経路 · Route change / bad ECMP member インターネットの経路が切り替わる数秒の間、または複数のECMP経路のうち不良な経路に割り当てられた接続で、パケットが消えます。 - なぜ → すると → 画面では:BGPの経路再計算、または複数の経路(ECMP・LAG)のうち1つの経路で機器・回線が不良 → 経路の切り替え中に一時的なパケットロス、またはその経路を通る接続だけに継続的なパケットロス → 突然数秒止まった後に早送り、または「再接続すると直る」(別の経路に割り当てられる) - 症状:フリーズ, 早送り, ワープ / 要因:パケットロス - 誰に:特定の地域・ISP / いつ:ときどきランダムに - 主担当:インフラチーム・ネットワークインフラ / 副担当:ゲーム開発チーム・サーバー開発, 外部・外部 - ゲーム開発チームの対応:接続ごとの再送統計(TCP_INFO)を記録し、症状が出ている人のIP・ポートと時刻を抽出できるようにする、数秒止まった接続をすぐに切断しない。 - インフラチームの対応:地域・通信事業者ごとの再送率を監視、再接続で経路が変わるかを確認、複数の通信事業者の回線を確保、自社機器のECMP・LAG経路のうち不良リンクを点検、経路の測定もゲームと同じTCPポートで行う(mtr --tcp --port。経路はアドレス・ポートで決まるため、通常のpingは別の経路を通って正常に見えることもある)。 - 外部の対応:通信事業者に、同じTCPポートで測った経路の測定結果と再接続前後の比較を添えて、不良経路を報告。 - グラフでは:ある時点から階段状に上昇(RTT(Ping)、地域・通信事業者ごとの再送率) - 確認箇所:bcc tcpretrans -cで再送を接続ごとに集計し、症状が出ているユーザーのアドレス・ポートを抽出。サーバーからユーザー側へ、ユーザー側からサーバーへ、ゲームと同じTCPポートでmtr(mtr -T -P PORT)を取って比較。再接続の前後の結果も比較 - 該当する場合:ある時刻を境に、特定の地域・通信事業者のRTTが階段状に変わって数秒間パケットロスが集中する。または同じ通信事業者の中でも一部の接続(アドレス・ポートの組み合わせ)だけが継続的に再送し、再接続すると直る。通常のpingは正常なのに、TCPのmtrでだけパケットロスが見えることもある - 該当しない場合:その通信事業者のすべての接続が夜のピークにそろって悪化するなら「ボトルネックでのキューあふれ」。1人のユーザーだけが悪く、ルーターまでのpingからパケットロスがあるなら「無線区間のパケットロス」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 実際の事例:cloudflare-2020 - 出典: - [RFC 2991: Multipath Issues in Unicast and Multicast Next-Hop Selection](https://www.rfc-editor.org/rfc/rfc2991) · IETF · マルチパスではping・tracerouteのような診断ツールの結果を信頼しにくいことと、フローをハッシュして経路を固定する方式の説明 - [RFC 2992: Analysis of an Equal-Cost Multi-Path Algorithm](https://www.rfc-editor.org/rfc/rfc2992) · IETF · ECMPは、フローを識別するヘッダーフィールドのハッシュで次の経路を選ぶ(同じフローは同じ経路) - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_INFO:ソケットごとの状態(struct tcp_info)を取得 - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · -T(--tcp)はICMPの代わりにTCP SYNを使い、-P(--port)は宛先ポートを指定 - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · 再送ごとに相手のアドレス・ポートを1行ずつ表示し、-cはフローごとの再送数を集計 #### rt-spurious-delay · 遅延の急上昇による不要な再送 · Spurious RTO from delay spikes パケットは消えておらず、一時的にとても遅れて届いただけなのに、その遅延がRTOより長いと、送信側はパケットロスと判断して再送します。 - なぜ → すると → 画面では:バッファブロート、Wi-Fiの省電力、モバイルの無線状態の切り替え、仮想マシンの一時停止で、瞬間的な遅延が数百ms → RTOが先に満了して再送、元のパケットもすぐに到着(受信側は重複して受け取る) → フリーズ・早送りは遅延の急上昇そのものが原因。不要な再送は止まる時間をほとんど延ばさず、再送のメトリクスだけを押し上げるため、パケットロスと誤解される - 症状:フリーズ, 早送り, 入力遅延 / 要因:遅延, ジッター - 誰に:自分だけ, サーバー全体 / いつ:ときどきランダムに, しばらく放置した後 - 主担当:外部・外部 / 副担当:インフラチーム・サーバーインフラ, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:Android 10以上のクライアントは、ゲーム中に低遅延Wi-Fiモード(WIFI_MODE_FULL_LOW_LATENCYのWi-Fiロック、画面がオンでゲームがフォアグラウンドにあるときだけ適用)を要求し、省電力による遅延の急上昇を減らす。 - インフラチームの対応:バースト可能インスタンスを避ける、RTOの最小値を下げすぎない、F-RTO・タイムスタンプを維持(tcp_frto、tcp_timestamps)、再送のメトリクスはnstatのTCPSpuriousRTOs・TCPDSACKRecvとあわせて確認し、パケットロスと誤解しない。 - 外部の対応:遅延の急上昇そのものを減らすため、ユーザーにルーターのSQMの利用、Wi-Fiの省電力の無効化を案内。 - 数値の目安:LinuxはF-RTOで不要なRTOを検知し、送信量の絞り込みを元に戻すこともあります。nstatのTCPSpuriousRTOs(不要なRTOと判定した回数)とTCPDSACKRecv(受信側が「もう受け取った」と知らせてきた回数)で確認します。 - グラフでは:不定期なスパイク(RTT(Ping)、不要なRTOの数) - 確認箇所:nstatを1分間隔で実行し、TcpExtTCPTimeouts(RTOの満了)、TcpExtTCPSpuriousRTOs、TcpExtTCPDSACKRecv、TcpExtTCPLostRetransmitの増分をあわせて確認。パケットキャプチャがあれば、Wiresharkのフィルターtcp.analysis.spurious_retransmissionを使う - 該当する場合:RTOが増えるときにTcpExtTCPSpuriousRTOsやTcpExtTCPDSACKRecvもそろって増え、同じ時刻にRTTが数百msに跳ねる。受信側のキャプチャに、元のパケットと再送の両方が届いている - 該当しない場合:TcpExtTCPSpuriousRTOs・DSACKは変わらないのにTcpExtTCPLostRetransmit(送り直したものまでまた失う)が増えるなら実際のパケットロス。RTTは跳ねないのにDSACKだけが継続的に多いなら「順序の入れ替わりによる不要な高速再送」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 5682: Forward RTO-Recovery (F-RTO): An Algorithm for Detecting Spurious Retransmission Timeouts with TCP](https://www.rfc-editor.org/rfc/rfc5682) · IETF · F-RTO:RTOの後に届いたACKで、不要なRTOだったかを見分ける - [RFC 3481: TCP over Second (2.5G) and Third (3G) Generation Wireless Networks](https://www.rfc-editor.org/rfc/rfc3481) · IETF · モバイル回線の遅延の急上昇(ハンドオーバー、リンクの回復など)が、不要なTCPタイムアウト・再送と輻輳ウィンドウの縮小を引き起こす - [RFC 2883: An Extension to the Selective Acknowledgement (SACK) Option for TCP](https://www.rfc-editor.org/rfc/rfc2883) · IETF · DSACK:受信側が重複受信を知らせることで、送信側が不要な再送に気づける - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPSpuriousRTOs(F-RTOが検知した不要なRTO)、TcpExtTCPDSACKRecv(受信したDSACKの数)、TcpExtTCPLostRetransmit(送り直したパケットをまた失ったとSACKが知らせてきた数) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_frtoはデフォルトで有効(RTTが揺れる無線ネットワークで有利)、tcp_timestampsのデフォルト値は1 - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · 不要な再送を防ぐには大きな最小RTOが必要だという根拠(最小1秒を推奨) - [WifiManager](https://developer.android.com/reference/android/net/wifi/WifiManager#WIFI_MODE_FULL_LOW_LATENCY) · Android (Google) · WIFI_MODE_FULL_LOW_LATENCY(API 29、Android 10):APに接続していて、画面がオンで、アプリがフォアグラウンドにあるときだけ適用される低遅延Wi-Fiロック - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatに表示されるカウンター名TCPTimeouts・TCPSpuriousRTOs・TCPDSACKRecv・TCPLostRetransmit - [net/ipv4/tcp_timer.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · TCPTimeoutsは再送タイマー(RTO)が満了したときに増加 - [nstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/nstat.8.html) · iproute2 · nstatはデフォルトで、前回の実行以降の増分を表示 - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · tcp.analysis.spurious_retransmissionの表示フィルター #### rt-reorder · 順序の入れ替わりによる不要な高速再送 · Reordering triggers spurious fast retransmit 複数の経路や束ねたリンクを通る間にパケットの順序が入れ替わると、受信側が重複ACKで「抜けたパケットがある」と知らせ、送信側は問題のないパケットを送り直します。 - なぜ → すると → 画面では:パケット単位で経路を振り分ける機器、パケット単位で振り分けて送るLAG(リンクアグリゲーション)、経路が切り替わる瞬間が順序を乱す → 後のパケットが先に到着して重複ACKが3つたまる → 高速再送 → まばらにやり取りされるゲームパケットにはほとんど影響なし。人の多い場所での大きな更新やアップデートデータのダウンロードが遅くなり、ときどきカクつく - 症状:カクつき, 入力遅延 / 要因:ジッター - 誰に:特定の地域・ISP, サーバー全体 / いつ:常に, 人が集中したとき - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ - インフラチームの対応:ネットワーク:パケット単位の分散の代わりに接続単位の分散(ECMP・LAGをアドレス・ポートのハッシュで)。サーバー機器・OS:RACK(時間に基づくロス判定、順序の入れ替わりに強い。DSACKで不要な再送を検知すると、順序の入れ替わりの許容幅を自動で広げる)を使う、Linuxが接続ごとに自動で推定した順序の入れ替わりの度合いを確認(ss -tiのreordering値、初期値はtcp_reordering=3)。 - グラフでは:最初から常に高い(順序の入れ替わりの検知数、DSACKの受信数) - 確認箇所:nstatのTcpExtTCPSACKReorder・TcpExtTCPTSReorder(順序の入れ替わりを検知した回数)とTcpExtTCPDSACKRecvを確認し、接続ごとにはss -tiのreordering(3でなければ表示)・reord_seenを確認。パケットキャプチャでは、Wiresharkのフィルターtcp.analysis.out_of_orderを使う - 該当する場合:順序の入れ替わりのカウンターとDSACKが時間帯に関係なく継続的に増え、特定の経路・機器を通る接続のreordering値が3より大きくなっている。受信側のキャプチャで、後のパケットが先に届き、前のパケットもすぐに届いている - 該当しない場合:順序の入れ替わりのカウンターは変わらず、TcpExtTCPLostRetransmitが増えるなら実際のパケットロス。RTTが跳ねる瞬間にだけDSACKが増えるなら「遅延の急上昇による不要な再送」 - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 2991: Multipath Issues in Unicast and Multicast Next-Hop Selection](https://www.rfc-editor.org/rfc/rfc2991) · IETF · パケット単位で経路を振り分けると順序が入れ替わり、後のパケットが3つ以上先に届くと、TCPは不要な高速再送を行う - [RFC 2992: Analysis of an Equal-Cost Multi-Path Algorithm](https://www.rfc-editor.org/rfc/rfc2992) · IETF · フローを識別するヘッダーフィールドのハッシュで経路を選ぶECMP(フロー単位の分散) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · 3つ目の重複ACKで高速再送 - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACKは時間に基づいてロスを判定するため順序の入れ替わりに強く、DSACKを受け取ると順序の入れ替わりの許容時間(reo_wnd)を延ばす - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_reorderingの初期値は3(接続ごとにtcp_max_reorderingまで自動調整)、tcp_recoveryのRACK設定 - [misc/ss.c](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/misc/ss.c?h=v6.12.0) · iproute2 · ss -tiは、接続のreordering値がデフォルト値の3と異なるときにreordering:値を、順序の入れ替わりを経験したことがあればreord_seen:回数を表示 - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPSACKReorder・TcpExtTCPTSReorder(順序の入れ替わりの検知)、TcpExtTCPDSACKRecv(受信したDSACKの数)、TcpExtTCPLostRetransmit(送り直したパケットをまた失った数) - [include/uapi/linux/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/tcp.h?h=v6.12) · Linux kernel · tcp_infoのtcpi_reord_seen:接続が経験した順序の入れ替わりの回数 - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · tcp.analysis.out_of_orderの表示フィルター #### rt-ack-path · ACKの遅れ・消失(上り回線の飽和) · ACK path congestion on asymmetric links データは問題なく届いているのに、「受け取った」というACKが満杯の上りキューで遅れたり消えたりすると、送信側はパケットロスと判断して再送します。 - なぜ → すると → 画面では:家で動画のアップロード・クラウドバックアップにより上り回線が満杯 → ACKがルーターのキューで数百ms遅れるか、あふれて破棄される → サーバーが送るゲームパケットはおおむね時間どおりに届く。同じ上りキューにたまった自分の入力が遅れて入力遅延・引き戻し、ときどき不要な再送 - 症状:入力遅延, 引き戻し / 要因:遅延, パケットロス - 誰に:同じ家 / いつ:ときどきランダムに, 夜のピーク時間帯 - 主担当:外部・外部 / 副担当:ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:Pingが急上昇したら画面にネットワーク状態を表示、「アップロード中のプログラムを確認」という案内を表示。 - 外部の対応:ユーザーに、ルーターのSQMで上りキューを短くする、小さなパケット(ACK)を優先処理する、アップロード速度を制限する(動画のアップロード・クラウドバックアップ)ことを案内。 - 数値の目安:ACKは後のACKが前のものの分まで確認してくれるため、いくつか消えるのはたいてい問題ありません。問題になるのは、キューで遅れるほうです。 - グラフでは:一部だけ高い(接続ごとのRTT(Ping)) - 確認箇所:ユーザーのPCで、アップロード(動画のアップロード・クラウドバックアップ)を実行した状態と止めた状態で、ゲームサーバーへのpingを比較。サーバーではss -tiでそのユーザーの接続のrttを確認 - 該当する場合:アップロード中にだけpingが数百msに上がって入力遅延・引き戻しが起き、アップロードを止めるとすぐに戻る。サーバーから見ると、そのときその接続のrttもそろって上がる - 該当しない場合:アップロードと関係なくパケットロスと遅延が起きるなら「無線区間のパケットロス」か経路側の原因。サーバーからユーザーへの方向だけが遅く、アップロードと関係ないなら「ボトルネックでのキューあふれ」 - 確認手段:ユーザー側の環境で確認 - 出典: - [RFC 3449: TCP Performance Implications of Network Path Asymmetry](https://www.rfc-editor.org/rfc/rfc3449) · IETF · 上りが細い非対称回線でACKが遅れたり消えたりするとTCPの性能が落ちる、ACKは累積確認なので一部が消えても後のACKが代わりを果たす、ACKの優先スケジューリングのような対策 - [Smart Queue Management](https://www.bufferbloat.net/projects/cerowrt/wiki/Smart_Queue_Management/) · Bufferbloat.net · ルーターでキュー管理とシェーピングを行い、キューを短く保つ - [tc-cake(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-cake.8.html) · iproute2 · CAKEはフローを分け、まばらに送るフロー(sparse flow)の遅延を最小化 - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ss -iのrtt(平均往復時間)とrttvar(偏差) #### rt-rto-setting · RTOの設定が環境に合っていない · RTO min too low or too high RTOの最小値を下げすぎると少し遅れただけで不要な再送が起き、デフォルト値(200ms)はゲームにとっては長すぎて、1回失うたびに長く止まります。 - なぜ → すると → 画面では:データセンター向けにRTOの最小値を大きく下げた、またはインターネット区間でデフォルト値のまま使用 → 低いと瞬間的な遅延でも再送が殺到、高いとパケットロスのたびに長く待つ → デフォルト値ならパケットロス1回で数百ms止まった後に早送り、下げすぎると止まる時間は減るが不要な再送が急増して回線を浪費 - 症状:フリーズ, 早送り, 入力遅延 / 要因:遅延 - 誰に:サーバー全体 / いつ:常に - 主担当:インフラチーム・サーバーインフラ / 副担当:ゲーム開発チーム・サーバー開発 - ゲーム開発チームの対応:Linux 6.15以降なら、ゲームの接続にTCP_RTO_MAX_MSでRTOの上限を下げることを検討(あきらめるまでの時間も短くなるため、TCP_USER_TIMEOUTで切断と判定する時間も一緒に決める)、サーバー間の内部接続だけソケットオプションTCP_RTO_MIN_US(6.15以降)でRTOの最小値を下げる、ソケットオプションTCP_THIN_LINEAR_TIMEOUTSでゲームの接続だけ連続したRTOが2倍に延びないようにすることを検討。 - インフラチームの対応:サーバー間の内部接続だけ経路ごとにrto_minを下げる、インターネット区間はデフォルト値を維持しつつRACK-TLP・thin streamの設定(tcp_thin_linear_timeouts)で補う。 - 数値の目安:LinuxのRTO = 往復時間 + max(200ms, RTTの偏差×4)。失敗するたびに2倍になり、最大120秒です。Linux 6.15以降は、TCP_RTO_MAX_MSでこの上限を1秒まで下げられます。 - グラフでは:最初から常に高い(接続ごとのRTO、不要なRTOの数) - 確認箇所:サーバーのRTO最小値の設定(ip route showのrto_min、Linux 6.11以降はsysctl net.ipv4.tcp_rto_min_us)とss -tiのrto・rttを確認し、nstatのTcpExtTCPSpuriousRTOsの増分を確認 - 該当する場合:最小値を下げたサーバーで、インターネット接続のrtoがrttにぴったり張り付いており、TcpExtTCPSpuriousRTOsが大きく増える。デフォルト値のままなら、ゲームの接続のrtoがrttより200ms以上大きく、パケットロス1回ごとにその分止まる - 該当しない場合:rtoがデフォルトの計算どおり(rtt + 200ms前後)で不要なRTOも少ないのに、止まる時間が際立って長いなら、連続したパケットロスか回復方式側の問題(「thin streamの遅い回復」「中間機器によるTCPオプションの除去」) - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · RTO = SRTT + max(G, 4·RTTVAR)、最小1秒を推奨、失敗するたびに2倍、最大値を設けるなら60秒以上 - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · LinuxのTCP_RTO_MINは200ms、TCP_RTO_MAXは120秒 - [net/ipv4/tcp_input.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_input.c?h=v6.12) · Linux kernel · LinuxのRTOはSRTT + rttvarで、rttvarはRTOの最小値(デフォルト200ms)を下回らない - [tcp: add the ability to control max RTO](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=54a378f43425085d0684679d99735696b69165bc) · Linux kernel · TCP_RTO_MAX_MSソケットオプションを追加(1〜120秒)、Linux 6.15から - [tcp: add sysctl_tcp_rto_min_us](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f086edef71be7174a16c1ed67ac65a085cda28b1) · Linux kernel · サーバー全体のデフォルトのRTO最小値tcp_rto_min_usを追加、Linux 6.11から - [tcp: support TCP_RTO_MIN_US for set/getsockopt use](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f38805c5d26fe4af97837c10d58074a7496638bf) · Linux kernel · ソケットごとにRTOの最小値を決めるTCP_RTO_MIN_USソケットオプションを追加、Linux 6.15から - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_usのデフォルト値は200000(経路オプションrto_min、ソケットオプションTCP_RTO_MIN_USが優先)、tcp_rto_max_msは1,000〜120,000(デフォルト値120,000)、tcp_thin_linear_timeouts - [ip-route(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ip-route.8.html) · iproute2 · 経路ごとのrto_minオプション:その宛先と通信するときのRTOの最小値 - [Thin-streams and TCP](https://docs.kernel.org/networking/tcp-thin.html) · Linux kernel · TCP_THIN_LINEAR_TIMEOUTSで、thin streamの接続だけ指数バックオフを無効にできる - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_USER_TIMEOUT:確認応答のないデータを待ち続け、接続を閉じるまでの時間 - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ss -iのrto(ms)とrtt - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPSpuriousRTOs:F-RTOが検知した不要なRTO #### rt-thin · thin streamの遅い回復 · Thin streams fall back to RTO ゲームのように小さなパケットをまばらに送ると、「後続のパケット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](https://docs.kernel.org/networking/tcp-thin.html) · Linux kernel · ゲームのようにまばらに送るthin streamでは高速再送がうまく働かず、長いタイムアウトに頼ることになる、ACK待ちのパケット(in-flight)が4個未満が基準 - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · 3つ目の重複ACKで高速再送 - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACKは後から送ったパケットが届いたことをもとにロスを判定、TLPの待ち時間は2・SRTT(確認応答のないパケットが1つだけなら遅延ACKの分の余裕を加える) - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_RTO_MINは200ms、thin streamの判定(in-flightのパケットが4個未満)と線形の再試行6回 - [tcp: remove thin_dupack feature](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4a7f6009441144783e5925551c72e3f2e1b0839b) · Linux kernel · 2017年1月にthin_dupackを削除(Linux 4.11)、RACKがその役割を担うという説明 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_thin_linear_timeouts:thin streamなら最大6回までRTOを2倍に延ばさない(デフォルトは無効) - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_NODELAYはNagleアルゴリズムを無効にする - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatに表示されるカウンター名TCPTimeouts・TCPFastRetrans・TCPLossProbes・TCPLossProbeRecovery - [net/ipv4/tcp_timer.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · TCPTimeoutsは再送タイマー(RTO)が満了したときに増加 - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPFastRetrans(Loss状態でないときの再送)、TcpExtTCPLossProbes(TLPを送信)・TcpExtTCPLossProbeRecovery(TLPでパケットロスを回復) - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ss -iのrto(ms)、backoff(RTOが2倍に延びた回数) #### rt-sack-stripped · 中間機器によるTCPオプションの除去 · Middlebox strips TCP options 一部のファイアウォール・高速化装置がTCPオプションを消したり書き換えたりすると、複数のパケットを失ったときに1往復に1つずつしか回復できなくなったり、ウィンドウ(一度に送れる量)が小さくなったりして遅くなります。 - なぜ → すると → 画面では:ファイアウォールの「TCP正規化」、古い高速化装置がSACK・タイムスタンプ・ウィンドウスケールのオプションを除去 → 失ったパケットが複数あると1往復ごとに1つずつ回復、ウィンドウは64KBに制限される → パケットロスのたびに止まる時間がずっと長くなり(SACKがないとRACK-TLPも使えない)、解消すると早送り。アップデートデータのダウンロードのような大容量の転送も遅い - 症状:フリーズ, 早送り / 要因:ストール, 遅延 - 誰に:特定の地域・ISP, サーバー全体 / いつ:常に - 主担当:インフラチーム・ネットワークインフラ / 副担当:インフラチーム・サーバーインフラ - インフラチームの対応:ネットワーク:該当機器のTCP正規化の設定を無効にする、ファイアウォールのシーケンス番号ランダム化も確認、両端のパケットキャプチャでSYNのオプションを比較。サーバー機器・OS:ss -tiでsack・wscaleの表示がない接続が特定の経路に偏っていないか確認(tsはWindows PCが設定によって使わないため、tsだけがないのは正常な場合がある)、サーバーのnet.ipv4.tcp_sackが1になっているか確認。 - グラフでは:最初から常に高い(SACKなしで開始した回復の数(TcpExtTCPRenoRecovery)) - 確認箇所:ss -tiで接続ごとにsack・wscaleの表示があるかを確認し、nstatのTcpExtTCPRenoRecovery(SACKなしで開始した回復)とTcpExtTCPSackRecoveryの比率、TcpExtTCPSACKDiscard(整合しないため破棄したSACKブロック数)を確認。疑わしい経路は両端でSYNをキャプチャし、オプション(Wiresharkのtcp.options.sack_permなど)を比較 - 該当する場合:特定の経路・機器を通る接続だけsack・wscaleがなく、TcpExtTCPRenoRecoveryの割合が高い。送信側でキャプチャしたSYNにあったSACK許可オプションが、受信側でキャプチャしたSYNにはない。シーケンス番号ランダム化が原因なら、オプションは残っているのにTcpExtTCPSACKDiscardが増える - 該当しない場合:すべての接続でsackがないなら、まずサーバーのnet.ipv4.tcp_sackの値を確認。オプションがそろっていてTcpExtTCPSACKDiscardも変わらなければ、回復が遅い理由は別にある(「thin streamの遅い回復」) - 確認手段:インフラのツールで確認(ゲームコード不要) - もっと詳しく:オプションが残っていても、SACKが壊れることがあります。ファイアウォールのシーケンス番号ランダム化(sequence randomization)がヘッダーのシーケンス番号だけを書き換え、SACK内の番号をそのままにすると、送信側は整合しないSACKを破棄します。2019年のSACKの脆弱性の際にサーバーでtcp_sack=0にして無効化し、そのまま忘れている場合も結果は同じです。 - 出典: - [RFC 2018: TCP Selective Acknowledgment Options](https://www.rfc-editor.org/rfc/rfc2018) · IETF · SACKなしの累積ACKだけでは、1往復ごとに失ったパケットを1つしか把握できない - [RFC 7323: TCP Extensions for High Performance](https://www.rfc-editor.org/rfc/rfc7323) · IETF · ウィンドウスケールオプションがなければ、ウィンドウは最大2^16 = 64KiB - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACK-TLPにはSACKの使用が必須 - [net/ipv4/tcp_output.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_output.c?h=v6.12) · Linux kernel · LinuxのTLPは、SACKを使う接続でのみスケジュールされる - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_sackのデフォルトは1(有効) - [misc/ss.c](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/misc/ss.c?h=v6.12.0) · iproute2 · ss -tiは、接続が使っているオプションに応じてts、sack、wscale:送信,受信を表示 - [tcp: limit payload size of sacked skbs](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3b4929f65b0d8249f19a50245cd88ed1a2f78cff) · Linux kernel · 2019年のSACK処理の脆弱性(CVE-2019-11477)の修正コミット - [Linux and FreeBSD Kernel: Multiple TCP-based remote denial of service vulnerabilities (NFLX-2019-001)](https://raw.githubusercontent.com/Netflix/security-bulletins/master/advisories/third-party/2019-001.md) · Netflix · 当時の暫定対策として、tcp_sack=0(SACK処理の無効化)が案内された - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPRenoRecovery(SACKなしで回復を開始)・TcpExtTCPSackRecovery(SACKで回復を開始)、TcpExtTCPSACKDiscard(無効なSACKブロックの数) - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · tcp.options.sack_perm(SYNのSACK許可オプション)の表示フィルター #### rt-zero-window · ゼロウィンドウ(再送のように見える停止) · Zero window, often mistaken for retransmission 受信側のプログラムがソケットを読むのが間に合わず、バッファが満杯になると、送信側は送信を止めてゼロウィンドウプローブだけを送ります。回線の問題ではありません。 - なぜ → すると → 画面では:クライアントのフレームが止まる、サーバーのスレッドがブロックされるなどでソケットを読めない → 受信ウィンドウが0になり、送信側は送信を止めてプローブだけを送る(間隔がだんだん延びる) → 止まった後に早送り。パケットキャプチャに「ZeroWindow」が見え、パケットロスはない - 症状:フリーズ, 早送り / 要因:ストール - 誰に:自分だけ, サーバー全体 / いつ:人が集中したとき, ときどきランダムに - 主担当:ゲーム開発チーム・クライアント開発 / 副担当:ゲーム開発チーム・サーバー開発, インフラチーム・サーバーインフラ - ゲーム開発チームの対応:パケットキャプチャでZeroWindowを送った側(ソケットを読めていない側)をまず確認、ネットワークの受信は別スレッドで読み続ける、受信バッファを適切なサイズに。クライアント:ロード・GCのようなフレーム停止の原因を解消。サーバー:ソケットを読むスレッドがブロックされる原因を解消。 - インフラチームの対応:サーバーのnstat TcpExtTCPToZeroWindowAdv(サーバーが受信ウィンドウを0と通知した回数)を監視に追加(増えればサーバー側の問題なのでサーバー開発に連絡)、サーバー側のパケットキャプチャを提供。 - グラフでは:途切れた後にまとめて到着(接続ごとの受信量、ゼロウィンドウの回数) - 確認箇所:パケットキャプチャでWiresharkのフィルターtcp.analysis.zero_windowを使い、ウィンドウ0を通知した側を探す。サーバーのnstatでは、TcpExtTCPToZeroWindowAdv(サーバーがウィンドウ0を通知)とTcpExtTCPWinProbe(相手のウィンドウ0に対してプローブを送信)を分けて確認し、サーバーのソケットのRecv-Q(ssで表示される、プログラムがまだ読んでいないバイト数)を確認 - 該当する場合:止まっている間に再送はなく、ゼロウィンドウとプローブだけがやり取りされる。サーバーのTcpExtTCPToZeroWindowAdvやサーバーのソケットのRecv-Qが増えるならサーバーの読み出しが間に合っていない。TcpExtTCPWinProbeが増えるならクライアントの読み出しが間に合っていない - 該当しない場合:キャプチャにゼロウィンドウがなく、同じデータが再度送られているなら、原因はパケットロスか不要な再送 - 確認手段:インフラのツールで確認(ゲームコード不要) - 実際の事例:roblox-2021 - 出典: - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · 受信ウィンドウが0なら送信側はゼロウィンドウプローブを送り、プローブの間隔は指数的に延ばす - [7.5. TCP Analysis](https://www.wireshark.org/docs/wsug_html_chunked/ChAdvTCPAnalysis.html) · Wireshark · TCP ZeroWindow:受信側がウィンドウ0を通知し、送信側に送信を止めさせたパケット - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · tcp.analysis.zero_window、tcp.analysis.zero_window_probeの表示フィルター - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPToZeroWindowAdv:受信ウィンドウを0以外の値から0に変えて通知した回数 - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatに表示されるカウンター名TCPToZeroWindowAdv・TCPWinProbe - [net/ipv4/tcp_output.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_output.c?h=v6.12) · Linux kernel · TCPWinProbe:相手の受信ウィンドウが0のときに送るプローブ(tcp_send_probe0)ごとに増加 - [net/ipv4/tcp_diag.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · ssのRecv-Qは、確立済みの接続では、受信したがプログラムがまだ読んでいないバイト数 #### rt-syn · 接続要求(SYN)の再送 · SYN retransmission on connect 接続要求が接続待ちキュー(backlog)のあふれやファイアウォールの遮断で消えると、クライアントOSは1秒後から間隔を延ばしながら送り直します。 - なぜ → すると → 画面では:メンテ明けの接続の殺到でサーバーの接続待ちキューがあふれる、またはファイアウォール・DDoS対策がSYNを破棄 → クライアントOSが1秒後から決まった間隔でSYNを再送(以前のLinuxは1秒 → 2秒 → 4秒) → 接続ボタンを押してから1秒、3秒のようにきりのいい秒数だけ遅れ、失敗が続くと接続不可・無限ロード - 症状:接続不可・無限ロード / 要因:パケットロス - 誰に:サーバー全体, 特定の地域・ISP / いつ:接続直後・メンテ明け - 主担当:ゲーム開発チーム・サーバー開発 / 副担当:インフラチーム・サーバーインフラ, インフラチーム・ネットワークインフラ, ゲーム開発チーム・クライアント開発 - ゲーム開発チームの対応:サーバー:listenのbacklog引数を増やす(somaxconnとあわせて)、ゲームサーバーがacceptを遅れずに呼ぶようにする、ログイン待機列の仕組み。クライアント:接続の再試行間隔を延ばす(ランダムに分散)。 - インフラチームの対応:サーバー機器・OS:サーバーの接続待ちキューのあふれはnstatのTcpExtListenOverflows・TcpExtListenDropsとログの「Possible SYN flooding」警告で確認、somaxconnを増やす(listenの引数とあわせて)、SYN Cookie。ネットワーク:ファイアウォール・DDoS対策のSYN制限を緩和。 - 数値の目安:Linux(Androidを含む)の最初のSYN再送は1秒後です。以前のカーネルはその後間隔を2倍ずつ延ばし、1、3、7、15秒…の時点で再送、6.5以降は1、2、3、4、5秒の時点で5回送り直した後に2倍ずつ(7、11、19秒…)間隔を延ばします(tcp_syn_linear_timeouts=4)。Androidスマホは、OSをアップデートしても発売時のカーネルを使い続けることが多いため、同じAndroidバージョンでも端末ごとに異なる場合があります。どちらの場合も、すべて失敗すると約2分後にあきらめます。Windowsはバージョンと設定によって1秒または3秒から延びていき、送り直す回数が2〜4回なので20〜30秒であきらめます(そのPCの値はnetsh int tcp show globalのMax SYN Retransmissionsで確認)。 - グラフでは:接続直後・メンテ明けに急増(接続試行数、接続待ちキューのあふれ数) - 確認箇所:サーバーのnstatでTcpExtListenOverflows・TcpExtListenDropsとdmesgの「Possible SYN flooding on port」警告を確認し、ss -lntで待ち受けソケットのRecv-Q(acceptを待っている接続数)がSend-Q(backlogの上限)に達していないか確認。サーバー側のキャプチャで、SYNが届いているか、SYN-ACKを返しているかを確認 - 該当する場合:メンテ明けの接続殺到時にTcpExtListenOverflowsが増え、Recv-QがSend-Qに張り付いている。キャプチャでは、同じクライアントのSYNが秒単位の間隔で再び届いているのに、サーバーが応答しない - 該当しない場合:SYNがサーバーまで届かず、サーバーのカウンターも変わらないなら、前段のファイアウォール・DDoS対策が破棄しているので、その機器のSYN制限・ドロップログを確認。サーバーがSYN-ACKを送っているのに接続が遅いなら、戻り方向のパケットロス - 確認手段:インフラのツールで確認(ゲームコード不要) - 出典: - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · 最初のRTO TCP_TIMEOUT_INIT = 1秒(RFC 6298の初期値) - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · 初期RTOは1秒、再送のたびに2倍 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_syn_retriesのデフォルトは6、tcp_syn_linear_timeoutsのデフォルトは4(SYNのRTOは1、1、1、1、1、2、4…)、最後の再送は67秒後・あきらめるのは131秒後、somaxconnのデフォルトは4096、tcp_syncookiesのデフォルトは1 - [tcp: make the first N SYN RTO backoffs linear](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ccce324dabfe2143519daf50ed8b1ef1d0c542f7) · Linux kernel · SYN再送の最初の部分を一定間隔に変えたコミット、Linux 6.5から(デフォルトの4はmacOS・iOSの方式にならったもの) - [Android common kernels](https://source.android.com/docs/core/architecture/kernel/android-common) · Android (Google) · 5.10〜6.18の共通カーネルが並行してサポートされ、以前のプラットフォーム向けのカーネル(例:android14-6.1)を新しいAndroid端末の発売やアップグレードに使える - [TcpMaxConnectRetransmissions](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc938209(v=technet.10)) · Microsoft · 以前のWindowsのデフォルト:SYN再送は2回、最初の待ち時間3秒から2倍ずつ、最後の再送の後さらに2倍の時間を待ってあきらめる(3+6+12=21秒) - [TCP/IP connectivity issues troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/tcp-ip-connectivity-issues-troubleshooting) · Microsoft · SYN再送の回数はOSによって異なり、netsh int tcp show globalのMax SYN Retransmissionsで確認 - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · listenのbacklog引数はsomaxconnで切り詰められる(Linux 5.4からデフォルト4096、それ以前は128) - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · acceptキューが満杯になるとSYNを破棄し、TcpExtListenOverflows・TcpExtListenDropsがそろって増える、TcpExtTCPSynRetrans - [net/ipv4/tcp_input.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_input.c?h=v6.12) · Linux kernel · 「Possible SYN flooding on port …」というログメッセージ - [net/ipv4/tcp_diag.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_diag.c?h=v6.12) · Linux kernel · 待ち受けソケットでは、ssのRecv-Qはacceptを待っている接続数、Send-Qはbacklogの上限 ## 章ごとの出典 ### #l-client-game - [Slow rendering](https://developer.android.com/topic/performance/vitals/render) · Android (Google) · 60FPSを出すには1フレームを16ms以内に描画する必要があり、遅れるとフレームが飛ばされてカクつきに見える - [Interpolation and extrapolation (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/interpolation.html) · Unity · 間隔を空けて届くスナップショットの間をつないで描画する補間、データが遅れたときに同じ方向・速度で動かし続ける外挿とその上限 - [Introduction to prediction (Netcode for Entities 6.5)](https://docs.unity3d.com/Packages/com.unity.netcode@6.5/manual/intro-to-prediction.html) · Unity · クライアントがサーバーの結果を待たずに自分の入力で先に動かす予測、サーバーと食い違えば補正 - [Peeking into VALORANT's Netcode](https://technology.riotgames.com/news/peeking-valorants-netcode) · Riot Games · 不規則に届くデータをバッファで均すと滑らかになるが、その分遅延が増え、推測が外れるとキャラクターが飛んだり滑ったりする - [Garbage collection modes](https://docs.unity3d.com/Manual/performance-incremental-garbage-collection.html) · Unity · 実験のGCスパイク:インクリメンタルGCを無効にすると、ヒープ全体を調べる間メインスレッドが止まり、1フレーム16msの上限を超える - [Scripting.GarbageCollector.CollectIncremental](https://docs.unity3d.com/ScriptReference/Scripting.GarbageCollector.CollectIncremental.html) · Unity · 実験のインクリメンタルGCのタイムスライス3ms:incrementalTimeSliceNanosecondsのデフォルト値は3ms - [Shader loading](https://docs.unity3d.com/Manual/shader-loading.html) · Unity · 実験の新しいエリアのロード:シェーダーバリアントを初めて使うとき、ドライバーがGPU向けに生成するため止まることがある - [Frame Pacing library](https://developer.android.com/games/sdk/frame-pacing) · Android (Google) · 実験のV-Syncの境界:60Hzの画面は、新しいフレームがなければ前のフレームをもう一度表示する - [Set fixed timestep to optimize physics simulation frequency](https://docs.unity3d.com/Manual/physics-optimization-cpu-frequency.html) · Unity · 実験の固定ステップの追いつき処理:フレームがステップ間隔より長いと1フレームでステップを複数回実行するため、負荷が大きくなる - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · 実験の追いつき処理の上限(実験では1フレームに5回まで):Unityは1フレームで進めるゲーム時間を最大1/3秒に制限して追いつき処理の悪循環を防ぎ、超えた分だけゲーム内の時計が遅れる ### #l-client-os - [Multitasking](https://learn.microsoft.com/en-us/windows/win32/procthread/multitasking) · Microsoft · スレッドごとにタイムスライス(約20ms、OS・CPUによって異なる)を与え、使い切ったら次のスレッドに切り替えるプリエンプティブマルチタスク - [Scheduling Priorities](https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities) · Microsoft · 実行可能なスレッドのうち、最も優先度の高いスレッドが順番に(ラウンドロビン)タイムスライスを受け取る - [Priority Boosts](https://learn.microsoft.com/en-us/windows/win32/procthread/priority-boosts) · Microsoft · 前面に表示したウィンドウ(フォアグラウンド)のプロセスの優先度を、バックグラウンドのプロセス以上に引き上げる - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · ソケットごとに受信バッファ(SO_RCVBUF)があり、デフォルト・最大サイズはシステム設定で決まる - [Wi-Fi low-latency mode](https://source.android.com/docs/core/connect/wifi-low-latency) · Android (Google) · Android 10以上のWi-Fi低遅延モードでは、フレームワークがWi-Fiの省電力(doze)を明示的に無効にする(アプリがフォアグラウンドにあり、画面がオンの場合) - [Cached apps freezer](https://source.android.com/docs/core/perf/cached-apps-freezer) · Android (Google) · Android 14以上では、キャッシュ状態のアプリを10秒後に凍結し、CPUを使えなくする - [Extending your app’s background execution time](https://developer.apple.com/documentation/uikit/extending-your-app-s-background-execution-time) · Apple · iOSはバックグラウンドに移ったアプリを数秒後に一時停止 - [_WDF_TIMER_CONFIG (wdftimer.h)](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdftimer/ns-wdftimer-_wdf_timer_config) · Microsoft · 実験のタイマー15.6ms:Windowsのシステムクロックティックのデフォルト間隔は15.6ms - [timeBeginPeriod function (timeapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod) · Microsoft · 実験のタイマー1ms:プログラムはtimeBeginPeriodでより高いタイマー分解能を要求できる - [Customize the Windows performance power slider](https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/customize-power-slider) · Microsoft · 実験の省電力モード:Windowsの電源モードは、性能を下げる代わりにバッテリー駆動時間を延ばす方向に電源・CPUの設定を変える - [Thermal API](https://developer.android.com/games/optimize/adpf/thermal) · Android (Google) · 実験のスマホの発熱:端末は高い性能を限られた時間しか維持できず、その後は発熱でスロットリングされる ### #l-memory - [Designs, Lessons and Advice from Building Large Distributed Systems (LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · 「数字の感覚」の表の根拠:L1キャッシュ0.5ns、L2 7ns、メインメモリ100ns、同じデータセンター内の往復0.5ms、ディスクのシーク10ms(2009年時点、表のキャッシュの数値はこれと少し異なる概算値) - [Solidigm™ D7-P5520 and D7-P5620 Product Brief](https://www.solidigm.com/products/data-center/product-briefs/d7-p5520-p5620-product-brief.html) · Solidigm · サーバー向けNVMe SSDの99.99%レイテンシ(four-nines latency)130µs:表のSSD読み込み・スワップからの読み戻しが100µs前後という根拠 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_usのデフォルトは200,000µs:LinuxのTCP再送の最小待ち時間200ms(表のTCP再送の行) - [What is NUMA?](https://docs.kernel.org/mm/numa.html) · Linux kernel · 別のCPU側(リモート)のメモリはローカルよりアクセスが遅く、帯域幅も低い(表のNUMAの行) - [JEP 439: Generational ZGC](https://openjdk.org/jeps/439) · OpenJDK · G1の停止は数ms〜数秒、ZGCの停止は1ms以下(本文のヒープ全体のGC数百ms〜数秒、表の大きなヒープのGC 1秒) - [Available Collectors](https://docs.oracle.com/en/java/javase/25/gctuning/available-collectors.html) · Oracle · ZGCはスループットを少し犠牲にして最大停止時間を1ms未満に抑え、停止時間はヒープサイズと無関係 - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · 世代別回収:Young領域だけを対象にするminor回収は短く、ヒープ全体を対象にするmajor回収ははるかに長くかかる(GC実験の世代別モード) - [The Z Garbage Collector](https://docs.oracle.com/en/java/javase/25/gctuning/z-garbage-collector1.html) · Oracle · ZGCは重い処理を並行して行うため1msを超えて止まることはないが、回収が追いつかないとアプリケーションがGCを待って止まることがある(GC実験の並行モード) - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · GCのマークフェーズがCPUの25%を使うためその間プログラムが遅くなり、アロケーションが多いとゴルーチンがGCを手伝う(assist)ために遅延する(GC実験の並行モード) - [Go 1.8 Release Notes](https://go.dev/doc/go1.8) · Go · GoのGCの停止は通常100µs未満 - [Debug a memory leak in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/debug-memory-leak) · .NET · GCがあっても、もう必要ないオブジェクトを参照し続けるとリークが起きる - [Concepts overview](https://docs.kernel.org/admin-guide/mm/concepts.html) · Linux kernel · メモリが不足するとページキャッシュ・スワップ可能なページを回収し、それでも足りなければOOM Killerがプロセスを強制終了(リーク実験) ### #l-disk - [Exos X18 Data Sheet](https://www.seagate.com/www-content/datasheets/pdfs/exos-x18-mango-DS2045-1N-2007US-en_US.pdf) · Seagate · 7,200rpmのHDDの4Kランダム読み込み170 IOPS、平均回転待ち時間4.16ms(本文の「HDDは150回ほど」、ディスク実験のHDD) - [D3-S4520 SSD](https://www.solidigm.com/products/data-center/d3/s4520.html) · Solidigm · SATA SSDの4KBランダム読み込み・書き込み最大92K/48K IOPS(本文の「SSDは数万回」) - [Solidigm™ D7-P5520 and D7-P5620 Product Brief](https://www.solidigm.com/products/data-center/product-briefs/d7-p5520-p5620-product-brief.html) · Solidigm · NVMe SSDのランダム読み込み・書き込み1,000K/200K IOPS(本文の「数十万回」) - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · gp3は標準で3,000 IOPS、gp2はI/Oクレジットで3,000 IOPSまでバーストし、クレジットが尽きるとベースライン性能に戻る - [Amazon EBS-optimized instance types](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-optimized.html) · AWS · 小さなインスタンスはEBSの最大性能を24時間に1回、30分間だけ出し、その後ベースライン性能に戻る(例:t4g.2xlargeはベースライン4,000・最大15,700 IOPS、ディスク実験のクラウドのバースト型の想定) - [Managed disk bursting](https://learn.microsoft.com/en-us/azure/virtual-machines/disk-bursting) · Microsoft Azure · 小さなディスク・VMはクレジットで最大30分バースト - [Concepts overview](https://docs.kernel.org/admin-guide/mm/concepts.html) · Linux kernel · ファイルに書いたデータはまずページキャッシュに入ってdirtyとマークされ、後からディスクに書き出される - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · 書き出し待ちのデータがdirty_ratioに達すると、書き込むプロセス自身がディスクへの書き出しを担う(ディスク実験のOSの書き込み上限) - [fsync(2) — Linux manual page](https://man7.org/linux/man-pages/man2/fsync.2.html) · Linux man-pages · fsyncはデバイスが書き込み完了を通知するまでブロック ### #l-db - [How MySQL Uses Indexes](https://dev.mysql.com/doc/refman/8.4/en/mysql-indexes.html) · MySQL · インデックスがなければ最初の行からテーブル全体を読む(フルスキャン) - [InnoDB Locking](https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html) · MySQL · 行ロックを取ったトランザクションが終わるまで、同じ行を更新しようとするリクエストは待たされる(ホットスポット) - [Number Of Database Connections](https://wiki.postgresql.org/wiki/Number_Of_Database_Connections) · PostgreSQL · DBのリソースを使い切った後は、接続を増やしてもスループットが落ちる(DB実験でプールを大きくしてもCPUコアが足りなければすべて遅くなることの根拠) - [WAL Configuration (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-configuration.html) · PostgreSQL · チェックポイントはデフォルトで5分またはWAL 1GBごとにダーティページをまとめて書き出す重い処理で、書き込みを分散してI/Oの集中を避ける - [Semisynchronous Replication](https://dev.mysql.com/doc/refman/8.4/en/replication-semisync.html) · MySQL · 非同期レプリケーションでプライマリが落ちると、コミット済みのトランザクションがスタンバイにない場合がある(切り替え後のロールバック) - [Log-Shipping Standby Servers (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/warm-standby.html) · PostgreSQL · ストリーミングレプリケーションはデフォルトで非同期なので、コミットからレプリカへの反映までに遅延がある(レプリカで、書いたばかりの内容が見えない) - [Asynchronous Commit (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-async-commit.html) · PostgreSQL · 書き込みをまとめて遅らせてディスクに反映すると速くなる代わりに、障害時に直近の変更を失うトレードオフ(数分ごとに保存するゲームサーバーと同じ構造) ### #judge - [Service Level Objectives (Site Reliability Engineering, ch. 4)](https://sre.google/sre-book/service-level-objectives/) · Google · 平均の代わりにパーセンタイル(50・95・99パーセンタイル)で遅延分布の形とテールを把握する - [The Tail at Scale](https://research.google/pubs/the-tail-at-scale/) · Google · たまに起きる長い遅延(テールレイテンシ)が、規模が大きくなるほどサービス全体の体感を左右する - [RFC 3550: RTP, A Transport Protocol for Real-Time Applications](https://www.rfc-editor.org/rfc/rfc3550) · IETF · 到着間隔ジッター(interarrival jitter)の定義と計算方法 - [RFC 1812: Requirements for IP Version 4 Routers](https://www.rfc-editor.org/rfc/rfc1812) · IETF · ルーターはTime ExceededなどのICMPエラーメッセージの送信頻度を制限できなければならず、Echo Replyも制限できる(mtr・pingの解釈に注意) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · icmp_ratelimit・icmp_ratemask:LinuxはTime Exceeded・Destination UnreachableなどのICMP応答をデフォルトでレート制限する - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ss -tiのrtt(平均往復時間)/rttvarフィールド - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · pidstat -t:スレッドごとのCPU使用率 - [bcc tools: runqlat examples](https://raw.githubusercontent.com/iovisor/bcc/master/tools/runqlat_example.txt) · IO Visor · ランキュー遅延(スレッドがCPUを待った時間)の分布の測定 - [RIPE Atlas documentation](https://atlas.ripe.net/docs/) · RIPE NCC · 世界中の測定拠点からping・tracerouteを実行する公開の外形監視ツール - [GeoLite2 Free Geolocation Data](https://dev.maxmind.com/geoip/geolite2-free-geolocation-data) · MaxMind · IPアドレスに国・ASNをひも付ける公開データベース - [View CloudWatch metrics for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · 基本モニタリングは5分、詳細モニタリングは1分間隔(集計間隔によって短いスパイクが隠れる) ### #l-nic - [NAPI](https://docs.kernel.org/networking/napi.html) · Linux kernel · デバイスが割り込みで新しいパケットを知らせると、カーネルがNAPIで取り出して処理、割り込みのコアレッシング(まとめて通知)は通常デバイス側で行う - [Scaling in the Linux Networking Stack](https://docs.kernel.org/networking/scaling.html) · Linux kernel · RSSで複数の受信キューを複数のコアに振り分ける仕組み、キューごとの割り込み、ハッシュによるキューの選択 - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · バッファがなくデバイスが破棄したパケット(rx_missed_errors)と、ethtool -Sのドライバーごとの統計 - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · リングバッファ(-G)、割り込みコアレッシング(-C)、受信ハッシュ(-N)、統計(-S)の設定・確認 - [How to receive a million packets per second](https://blog.cloudflare.com/how-to-receive-a-million-packets/) · Cloudflare · キュー1つ・コア1つでは毎秒約35万〜43万パケットで頭打ちになり、キューとコアを増やして初めて100万ppsを受信できたという測定(シミュレーションはコアあたりの処理量をこれより余裕のある70万ppsと仮定) - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · クラウドインスタンスの帯域幅・PPS・接続追跡の上限を超えると、インスタンスの外でキューイングした後に破棄、上限超過カウンター ### #l-server-os - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · 接続待ちキュー(backlog)とsomaxconnの上限(5.4からデフォルト4,096、それ以前は128) - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · Linuxはacceptキューが満杯になると接続要求(SYN)を破棄し、TcpExtListenOverflowsを増やす - [listen function (winsock2.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen) · Microsoft · Windowsはキューが満杯になるとクライアントにWSAECONNREFUSEDを返す - [getrlimit(2) — Linux manual page](https://man7.org/linux/man-pages/man2/getrlimit.2.html) · Linux man-pages · RLIMIT_NOFILE:プロセスが開けるfd数の上限、超えるとEMFILE - [systemd-system.conf(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd-system.conf.5.html) · systemd · サービスのfd上限のデフォルト値1024:524288(シミュレーションの「fd 1,024」の設定ミス) - [The /proc Filesystem](https://docs.kernel.org/filesystems/proc.html) · Linux kernel · OOM Killerはメモリ使用量の割合から算出したスコア(badness)で強制終了するプロセスを選び、oom_score_adjで調整できる - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · memory.maxに達して減らせなければそのcgroup内でOOM Killerが動く、cpu.maxでCPUの上限を設定 - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · steal:仮想化環境で他のOSに奪われたCPU時間 - [Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) · AWS · 指数バックオフだけでは再試行が集中し、ランダム性(ジッター)を加えることで競合が減る(シミュレーションのリトライ方式) ### #l-socket - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · TCPは順序を守る信頼性のあるバイトストリーム、Nagleアルゴリズムと遅延ACKの定義 - [RFC 768: User Datagram Protocol](https://www.rfc-editor.org/rfc/rfc768) · IETF · UDPは到達と重複の排除を保証しない - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · 信頼性・順序が必要なUDPアプリは自前で実装する必要がある - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_NODELAY、TCP_USER_TIMEOUT、keepaliveなどのTCPソケットオプション - [socket(7) — Linux manual page](https://man7.org/linux/man-pages/man7/socket.7.html) · Linux man-pages · SO_SNDBUF・SO_RCVBUF・SO_KEEPALIVE・SO_LINGERのソケットオプション - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · 重複ACK3つで高速再送、タイマーによる再送の後は輻輳ウィンドウが1セグメント(シミュレーションの教科書どおりのルール) - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · 重複ACKの数の代わりに送信時刻でロスを判定するRACK - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · RTOは最小1秒を推奨、満了のたびに2倍にバックオフ - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · Linuxのtcp_rto_min_usのデフォルトは200ms、ロス検知はRACK(tcp_recovery) - [include/net/tcp.h (Linux v6.12)](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · シミュレーションのLinuxの遅延ACK 40ms:TCP_DELACK_MIN(HZ/25 = 40ms)、最大TCP_DELACK_MAX(HZ/5 = 200ms) - [Design issues - Sending small data segments over TCP with Winsock](https://learn.microsoft.com/en-us/previous-versions/troubleshoot/windows/win32/data-segment-tcp-winsock) · Microsoft · シミュレーションのWindowsの遅延ACK 200ms:データを受け取ると200msの遅延ACKタイマーをセットし、Nagleアルゴリズムと重なると小さなパケットがACKを待つ - [RFC 896: Congestion Control in IP/TCP Internetworks](https://www.rfc-editor.org/rfc/rfc896) · IETF · Nagleアルゴリズムの本来の目的:キー入力1バイトごとに41バイトのパケットが送出されていたリモート端末の問題 - [send(2) — Linux manual page](https://man7.org/linux/man-pages/man2/send.2.html) · Linux man-pages · 送信バッファが満杯になると、ブロッキングモードのsend()は戻らない ### #journey - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · 光ファイバーの伝送遅延5µs/km(1,000kmで片道5ms)、片道150ms以下ならほとんどのアプリケーションでほぼ影響がないが、インタラクティブ性の高い作業では100ms未満でも影響がある - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · 実験のサーバー所在地ごとのインターネット区間の遅延:ソウルを起点に釜山地域8ms、東京30ms、シンガポール68ms、米国西海岸124〜136ms、ヨーロッパ234〜244ms(往復の中央値) - [The Internet at the Speed of Light (HotNets 2014)](https://conferences.sigcomm.org/hotnets/2014/papers/hotnets-XIII-final111.pdf) · ACM · 実験の経路倍率1.5:実際のルーター経路は、光ファイバーの直線距離と比べて中央値で約1.5倍 - [Report ITU-R M.2134: Requirements related to technical performance for IMT-Advanced radio interface(s)](https://www.itu.int/pub/R-REP-M.2134-2008) · ITU · LTE-Advancedの無線区間の片方向遅延の要求値は10ms未満(無負荷・小さいパケットの場合。実験のLTEの値は、これに負荷とスケジューリング待ちを加えた想定) - [Report ITU-R M.2410: Minimum requirements related to technical performance for IMT-2020 radio interface(s)](https://www.itu.int/pub/R-REP-M.2410-2017) · ITU · 5G(IMT-2020)の無線区間の片方向遅延の要求値は4ms(eMBB、無負荷の場合) - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · 実験のルーターのキュー:家庭用ルーターは上りと下りの通信が重なると、キューでの遅延が数百ms(最大約400ms)まで増えることがある - [Maximum transmission unit and maximum segment size](https://developers.cloudflare.com/magic-transit/reference/mtu-mss/) · Cloudflare · 実験のDDoS対策経由:入ってくるトラフィックだけが防御網を経由し、サーバーの応答はインターネットに直接出ていく(DSR) ### #l-home - [RFC 7567: IETF Recommendations Regarding Active Queue Management](https://www.rfc-editor.org/rfc/rfc7567) · IETF · 機器のキューがたまることがインターネットの遅延の主な原因であり、キュー管理(AQM)をデフォルトで使うべきという推奨 - [RFC 8289: Controlled Delay Active Queue Management](https://www.rfc-editor.org/rfc/rfc8289) · IETF · CoDelの目標待ち時間5ms・観測間隔100ms(実験のSQMのキュー5ms前後) - [RFC 8290: The Flow Queue CoDel Packet Scheduler and Active Queue Management Algorithm](https://www.rfc-editor.org/rfc/rfc8290) · IETF · FQ-CoDel:アドレス・ポートでフローごとにキューを分け、キューを作らない小さなフローを先に送り出す - [What Can I Do About Bufferbloat?](https://www.bufferbloat.net/projects/bloat/wiki/What_can_I_do_about_Bufferbloat/) · Bufferbloat.net · cake・fq_codelのようなSQMに対応したルーターを使い、負荷をかけた状態の遅延を測りながらSQMの速度を調整する - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · 家庭用ルーター34機種の測定:上り・下りが重なるときのキューでの遅延は最大約400ms、UDPマッピングの保持時間の中央値は90秒 - [Ending the Anomaly: Achieving Low Latency and Airtime Fairness in WiFi (USENIX ATC 2017)](https://www.usenix.org/system/files/conference/atc17/atc17-hoiland-jorgensen.pdf) · USENIX · Wi-Fiの無線キューでも負荷時に数百msの遅延、遅い端末がほかの端末の送信時間まで食いつぶす - [RFC 4787: Network Address Translation (NAT) Behavioral Requirements for Unicast UDP](https://www.rfc-editor.org/rfc/rfc4787) · IETF · NATのUDPマッピングのタイマーの要件(2分以上、デフォルトは5分以上を推奨)と、内側から出ていくパケットによる更新 - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · Wi-Fiは、チャンネルが使用中なら送信を待ち、ランダムなバックオフの後に送るCSMA/CA方式 - [Resolve Wi-Fi and Bluetooth issues caused by wireless interference](https://support.apple.com/en-us/102319) · Apple · 実験の電子レンジの干渉:電子レンジ・Bluetoothなどが2.4GHzのWi-Fiを妨害し、5GHzに移ると改善する - [RFC 9438: CUBIC for Fast and Long-Distance Networks](https://www.rfc-editor.org/rfc/rfc9438) · IETF · 実験のアップロード速度の調整:CUBICはパケットロスが起きると送信ウィンドウを0.7倍(約30%減)に縮め、再び広げる ### #l-isp - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · 光ファイバーの伝送遅延5µs/km:光は光ファイバー中を秒速約20万kmで進み、1,000kmの往復は最低10ms - [The Internet at the Speed of Light (HotNets 2014)](https://conferences.sigcomm.org/hotnets/2014/papers/hotnets-XIII-final111.pdf) · ACM · 実際のルーター経路は光ファイバーの直線距離と比べて中央値で約1.5倍、最小Pingは光速で計算した値の3.2倍(光ファイバーの直線距離と比べて約2倍) - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · ソウルを起点とした実測の往復中央値:東京30ms、米国西海岸124〜136ms、ヨーロッパ234〜244ms(韓国とヨーロッパの間は光ファイバーの直線距離の約2.8倍) - [AAE-1 & SMW5 cable cuts impact millions of users across multiple countries](https://blog.cloudflare.com/aae-1-smw5-cable-cuts/) · Cloudflare · ヨーロッパとアジアの間のトラフィックはたいていエジプトを通り、海底ケーブルの修理には数日〜数週間かかる - [Quantifying the Causes of Path Inflation (SIGCOMM 2003)](https://conferences.sigcomm.org/sigcomm/2003/papers/p113-spring.pdf) · ACM · 通信事業者間のピアリングポリシーとドメイン間ルーティングが経路を大きく延ばす - [Inferring Persistent Interdomain Congestion (SIGCOMM 2018)](https://www.caida.org/catalog/papers/2018_inferring_persistent_interdomain_congestion/inferring_persistent_interdomain_congestion.pdf) · ACM · 一部の通信事業者間の接続区間では、毎日ピーク時間帯になるたびに遅延とパケットロスが増える、繰り返し発生する輻輳が見られる - [RFC 4271: A Border Gateway Protocol 4 (BGP-4)](https://www.rfc-editor.org/rfc/rfc4271) · IETF · インターネットの経路情報をやり取りするBGP、ホールドタイムの推奨デフォルト値は90秒 - [BGP updates in 2024](https://blog.apnic.net/2025/01/07/bgp-updates-in-2024/) · APNIC · 経路が変わってからルーティングが再び安定するまでの時間は、日ごとの平均でIPv4が25〜35秒、IPv6が40〜50秒 - [Delayed Internet Routing Convergence (SIGCOMM 2000)](https://conferences.sigcomm.org/sigcomm/2000/conf/paper/sigcomm2000-5-2.pdf) · ACM · 経路障害の後、収束に数分かかることがあり、その間はパケットロス・遅延が増える(2000年当時の測定) ### #l-dc-net - [High-Resolution Measurement of Data Center Microbursts (IMC 2017)](https://conferences.sigcomm.org/imc/2017/papers/imc17-final60.pdf) · ACM · データセンターネットワークのエンドツーエンドの遅延は1ms未満で、バーストの70%以上は数十µs以内に終わるため平均利用率では見えない - [Data Center TCP (DCTCP) (SIGCOMM 2010)](https://conferences.sigcomm.org/sigcomm/2010/papers/sigcomm/p63.pdf) · ACM · 汎用スイッチは複数のポートで浅いバッファを共有しており、短い間に複数のフローが1つのポートに集中するとバッファがあふれてパケットロスが起きる - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · 接続追跡テーブルの最大エントリ数と保持時間のデフォルト値(UDP 30秒・ストリーム120秒、確立済みTCP 5日) - [Edit attributes for your Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-load-balancer-attributes.html) · AWS · ALBのアイドルタイムアウトはデフォルト60秒、時間が来るとロードバランサーが接続を閉じる - [Network Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html) · AWS · 実験のロードバランサーのデフォルト値:NLBはTCP 350秒、UDP 120秒(変更不可)、アイドル後は黙って追跡をやめる - [Configure load balancer TCP reset and idle timeout](https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-tcp-idle-timeout) · Microsoft Azure · Azure Load Balancerのアイドルタイムアウトはデフォルト4分 - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · セキュリティグループの接続追跡のデフォルト値、ロードバランサー・ファイアウォールのTCPアイドルタイムアウトは一般に60〜90分という説明 - [Flow-Based Sessions](https://www.juniper.net/documentation/us/en/software/junos/flow-packet-processing/topics/topic-map/security-flow-based-session-for-srx-series-devices.html) · Juniper Networks · 実験の企業ファイアウォール:SRXファイアウォールのデフォルトのセッションタイムアウトはTCP 1,800秒(30分)、UDP 60秒 - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · 実験の家庭用ルーターのTCP 1時間:TCPマッピングの中央値は約60分。UDPマッピングは機種によって30〜691秒とばらつき、中央値は片方向で90秒、双方向で約180秒 - [A Multi-perspective Analysis of Carrier-Grade NAT Deployment (IMC 2016)](https://www.icir.org/vern/papers/cgn-imc16.pdf) · ACM · 実験のCGNAT UDP 30秒・家庭用ルーターUDP 1分:CGNのUDPマッピングの中央値は固定回線で35秒・モバイル回線で65秒、測定したNATの74%が1分以下、家庭用ルーター(CPE)のNATはほとんどが65秒 - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · 実験のTCP keepalive:デフォルトでは2時間(7,200秒)アイドルの後、75秒間隔で9回確認し、応答がなければ切断 - [Cached apps freezer](https://source.android.com/docs/core/perf/cached-apps-freezer) · Android (Google) · 実験のモバイルのバックグラウンド:Android 14以上は、キャッシュ状態に入ったアプリのプロセスを10秒後に凍結 - [RFC 5880: Bidirectional Forwarding Detection (BFD)](https://www.rfc-editor.org/rfc/rfc5880) · IETF · ルーティングプロトコルの秒単位のHelloより速く経路の障害を検知するBFD - [RFC 2923: TCP Problems with Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc2923) · IETF · ICMPが遮断され、大きなパケットだけが消える経路MTUブラックホール ### #basics - [Teletraffic Engineering Handbook (ITU-D Study Group 2 Question 16/2)](https://www.itu.int/dms_pub/itu-d/opb/stg/D-STG-SG02.16.2.1-2002-PDF-E.pdf) · ITU · M/M/1の平均待ち時間W = A·s/(1−A):利用率50%・80%・90%で処理時間の1・4・9倍。同じ利用率なら、ワーカー(サーバー)が多いほど、到着が均一なほど待ち時間が短い - [CloudWatch metrics that are available for your instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html) · AWS · EC2のCPUUtilizationはインスタンス全体の値で、基本モニタリングは5分、詳細モニタリングは1分単位で集計 - [Designs, Lessons and Advice from Building Large Distributed Systems (Jeff Dean, LADIS 2009 keynote)](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · メインメモリの参照100ns、同じデータセンター内の往復500,000ns(0.5ms) - [ITU-T G.114: One-way transmission time](https://www.itu.int/rec/T-REC-G.114-200305-I/en) · ITU · 光ファイバーの伝搬遅延5µs/km(光速の限界を計算する根拠) - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · Half-Lifeのデフォルト値:毎秒20回の更新、補間100ms。毎秒10回なら補間200msで1回の欠落に耐えられる - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_usのデフォルト値は200000(200ms) - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · 単純反応時間の平均は約231ms(機器の遅延を補正すると213ms) - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · 100ms未満の遅延でもゲームのタスクの成績に影響し、ドラッグのような操作では約10msでも気づく - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · 熟練したプレイヤーはブラインドテストで約10msの差にも気づく - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · ジャンル別の遅延の許容値:一人称視点は約100ms、三人称視点(RPG・MMO)は約500ms、RTSは約1,000ms - [JEP 333: ZGC: A Scalable Low-Latency Garbage Collector](https://openjdk.org/jeps/333) · OpenJDK · G1は128GBのヒープで一時停止が平均157ms・最大544ms、ZGCはヒープや生存データのサイズに関係なく約1〜2ms - [CommonNetworkParametersExtensions (Unity Transport 2.5)](https://docs.unity3d.com/Packages/com.unity.transport@2.5/api/Unity.Networking.Transport.CommonNetworkParametersExtensions.html) · Unity · disconnectTimeoutMS:この時間、何も受信しなければ接続を切断(デフォルト30,000ms) ### #lab - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · 補間は過去の状態を描く代わりに滑らかで、外挿は方向転換を予測できないため外れると飛ぶ。予測の誤差はサーバーの結果で補正 - [UDP vs. TCP](https://gafferongames.com/post/udp_vs_tcp/) · Gaffer On Games · TCPはパケットを1つ失うと、新しいデータが届いても再送されるまでアプリに渡さない(通常2×RTT以上) - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · 確認応答のない入力を毎回のパケットに重複して載せれば、再送を待たずに済む(最悪の場合で2秒分) - [Snapshot Interpolation](https://gafferongames.com/post/snapshot_interpolation/) · Gaffer On Games · 受け取ってすぐ描画するとジッターでカクつき、補間バッファは遅延を少し増やす代わりに滑らかにする - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · クライアントの移動が接続の問題で欠けたり誤っていたりすると、サーバーが位置を補正(引き戻し) - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · ティックごとにたまるコマンドバジェットで、まとめて届いたコマンドを制限。厳しすぎると正常なユーザーもカクつく - [Introducing Time Dilation (TiDi)](https://www.eveonline.com/news/view/introducing-time-dilation-tidi) · CCP Games · サーバーの過負荷時にゲーム内の時計を遅らせて(Time Dilation)、すべてをゆっくり進める設計 - [CommonNetworkParametersExtensions (Unity Transport 2.5)](https://docs.unity3d.com/Packages/com.unity.transport@2.5/api/Unity.Networking.Transport.CommonNetworkParametersExtensions.html) · Unity · 一定時間何も受信しなければ接続を切断する無通信タイムアウト ### #symptoms - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · 更新が欠けると最後の位置で止まる(カクつき)か、外挿した後に飛ぶ(ワープ)。外挿する時間は制限すべき - [Understanding Networked Movement in the Character Movement Component for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine) · Epic Games · クライアントの移動が欠けたりサーバーの計算と食い違ったりすると、サーバーが補正を送って位置を戻す(引き戻し) - [UDP vs. TCP](https://gafferongames.com/post/udp_vs_tcp/) · Gaffer On Games · TCPは失ったパケットが再送されてくるまで後続のデータをためておき、まとめて渡す(早送り) - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · たまった入力が一度に届くと、複数フレーム分をまとめて計算して追いつく - [Introducing Time Dilation (TiDi)](https://www.eveonline.com/news/view/introducing-time-dilation-tidi) · CCP Games · サーバーが過負荷のときにゲーム内の時計を遅らせるTime Dilation(TiDi)、過負荷時に処理が数秒ずつ遅れる現象 - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · バッファリング時間は、サーバーのティックレートとクライアントの描画フレームによって変わる - [Using Gameplay Abilities in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-gameplay-abilities-in-unreal-engine) · Epic Games · 予測で実行したアビリティをサーバーが取り消すことがある(不発・ロールバック) - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · サーバーが関連なしと判断したアクターはレプリケートされないか、クライアントから削除される(表示されない・ゴースト) - [CommonNetworkParametersExtensions (Unity Transport 2.5)](https://docs.unity3d.com/Packages/com.unity.transport@2.5/api/Unity.Networking.Transport.CommonNetworkParametersExtensions.html) · Unity · 無通信タイムアウトを過ぎると接続を切る(切断) ### #sync - [Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (Yahn W. Bernier, GDC 2001)](https://web.cs.wpi.edu/~claypool/courses/4513-B03/papers/games/bernier.pdf) · Valve · サーバー権威型、クライアントサイド予測・サーバー補正、補間、ラグコンペンセーションの原理と、「角の向こうで撃たれる」のようなトレードオフ - [What Every Programmer Needs To Know About Game Networking](https://gafferongames.com/post/what_every_programmer_needs_to_know_about_game_networking/) · Gaffer On Games · P2Pのロックステップからクライアント/サーバー、クライアントサイド予測へと続いたネットコードの発展 - [Using Gameplay Abilities in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-gameplay-abilities-in-unreal-engine) · Epic Games · Local PredictedとServer Initiatedの、反応性と正確性のトレードオフ - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · サーバー権威・予測・バッファリング・巻き戻し判定と、巻き戻しの上限 - [NetworkTime and ticks (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/advanced-topics/networktime-ticks.html) · Unity · サーバー時刻に合わせてイベントを予約して再生する方法 - [1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond](https://www.gamedeveloper.com/programming/1500-archers-on-a-28-8-network-programming-in-age-of-empires-and-beyond) · Game Developer · ロックステップ:コマンドを2ターン後に予約、最も遅いコンピューターに合わせたターンの長さ、一定の500msの遅延は問題ないが、ばらつく遅延は不快 - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · ロックステップはすべての入力がそろわないと進まない、再生遅延バッファでジッターを吸収 - [GGPO Rollback Networking SDK](https://www.ggpo.net/) · GGPO · ロールバック:相手の入力を予測して進め、外れたら巻き戻して再計算 - [8 Frames in 16ms: Rollback Networking in 'Mortal Kombat' and 'Injustice 2'](https://www.gdcvault.com/play/1025471/8-Frames-in-16ms-Rollback) · GDC · ロールバックはロックステップのローカル入力遅延をなくす、最大8フレームを再計算 - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · 同じ遅延でも、行動の精度・締め切り時間と視点(一人称・三人称・俯瞰)によって影響が異なる - [Networking Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-overview-for-unreal-engine) · Epic Games · リッスンサーバーのホストの有利さと負荷 - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · 人間の単純反応時間は約0.23秒 ### #partial - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · 1人がずれるとその人にだけ補正をかけ、残りの9人は滑らかに見える。巻き戻し判定には上限を設ける - [State Synchronization](https://gafferongames.com/post/state_synchronization/) · Gaffer On Games · パケットはフレームごとに2個、0個のように偏って届き、ジッターバッファで均す - [Source SDK 2013: player.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player.cpp) · Valve · まとめて届いたコマンドをティックに分けて処理するコマンドバジェット、厳しい制限の副作用 - [Source SDK 2013: player_lagcompensation.cpp](https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/master/src/game/server/player_lagcompensation.cpp) · Valve · Sourceエンジンの巻き戻しの上限sv_maxunlagはデフォルト1秒 - [A Survey and Taxonomy of Latency Compensation Techniques for Network Computer Games (ACM Computing Surveys, 2022)](https://web.cs.wpi.edu/~claypool/papers/lag-taxonomy/paper.pdf) · ACM · ラグコンペンセーションで起きる「角の向こうで撃たれる」現象、全員の入力をそろえるための入力遅延 - [send(2) — Linux manual page](https://man7.org/linux/man-pages/man2/send.2.html) · Linux man-pages · 送信バッファが満杯になると、send()はブロッキングモードでは待ち、ノンブロッキングならEAGAINですぐに戻る - [Networking Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-overview-for-unreal-engine) · Epic Games · リッスンサーバーのホストはほかのプレイヤーより有利 - [Authority (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/manual/terms-concepts/authority.html) · Unity · 分散権限:クライアントごとに一部のオブジェクトを受け持って計算 - [Actor Relevancy in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-relevancy-in-unreal-engine) · Epic Games · サーバーは接続ごとに関連のあるアクターだけを送り、関連がなくなればクライアントから削除 - [NetworkConfig class (Netcode for GameObjects 2.5)](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.5/api/Unity.Netcode.NetworkConfig.html) · Unity · まだ生成されていないオブジェクト宛てのメッセージは保留し、時間が過ぎると破棄(SpawnTimeout) - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · フラグメント化されたUDPパケットは、フラグメントを1つ失うだけで丸ごと失われる - [Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE](https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse) · Microsoft · 同じポートを2つのソケットが共有すると、どちらがパケットを受け取るか分からない - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · IPアドレスを複数の契約者が共有すると、IPだけではユーザーを区別できない - [Application.runInBackground](https://docs.unity3d.com/ScriptReference/Application-runInBackground.html) · Unity · バックグラウンドでアプリが一時停止するデフォルトの動作 - [Actor Priority in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-priority-in-unreal-engine) · Epic Games · 帯域幅が飽和すると、優先度に応じて一部のアクターだけをレプリケート ### #retrans - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · RTO = SRTT + max(G, 4·RTTVAR)、初期値1秒、最小1秒を推奨、満了するたびに2倍、上限を設けるなら60秒以上、再送したパケットはRTTのサンプルから除外(Karnのアルゴリズム) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · 3つ目の重複ACKで高速再送、RTOの後は輻輳ウィンドウ1セグメント(ロスウィンドウ)から再開 - [RFC 6675: A Conservative Loss Recovery Algorithm Based on Selective Acknowledgment (SACK) for TCP](https://www.rfc-editor.org/rfc/rfc6675) · IETF · SACK情報で抜けたパケットを判断するロス回復(高速再送のきっかけになる重複ACK・SACKのシグナル) - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACKの順序の入れ替わりに対する猶予(min_RTT/4)とDSACKによる調整、TLPの待ち時間2・SRTT(確認応答のないパケットが1つだけなら遅延ACKの分の余裕を加える)、TLPを送った後のRTOの再設定、SACK必須 - [RFC 2018: TCP Selective Acknowledgment Options](https://www.rfc-editor.org/rfc/rfc2018) · IETF · SACK:受信側が途中の抜けを通知 - [RFC 2883: An Extension to the Selective Acknowledgement (SACK) Option for TCP](https://www.rfc-editor.org/rfc/rfc2883) · IETF · DSACK:すでに受け取ったものをまた受け取ったと知らせ、不要な再送を明らかにする - [RFC 5682: Forward RTO-Recovery (F-RTO): An Algorithm for Detecting Spurious Retransmission Timeouts with TCP](https://www.rfc-editor.org/rfc/rfc5682) · IETF · F-RTO:不要なRTOの検知 - [RFC 3522: The Eifel Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc3522) · IETF · タイムスタンプで回復が不要だったかを事後に見分ける(シミュレーションの輻輳ウィンドウの復元) - [RFC 7323: TCP Extensions for High Performance](https://www.rfc-editor.org/rfc/rfc7323) · IETF · タイムスタンプ・ウィンドウスケールオプション、スケールがなければウィンドウは最大64KiB - [RFC 6937: Proportional Rate Reduction for TCP](https://www.rfc-editor.org/rfc/rfc6937) · IETF · PRR:回復中に送る量を、新たに届いた量に合わせて減らす(シミュレーションの回復中の送信上限) - [RFC 9438: CUBIC for Fast and Long-Distance Networks](https://www.rfc-editor.org/rfc/rfc9438) · IETF · CUBICはパケットロス時に輻輳ウィンドウを0.7倍に縮める(シミュレーションの30%縮小) - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · ゼロウィンドウプローブ:ウィンドウが0でもプローブを送り、間隔は指数的に延ばす - [RFC 9000: QUIC: A UDP-Based Multiplexed and Secure Transport](https://www.rfc-editor.org/rfc/rfc9000) · IETF · QUICでは、パケットロスはそのパケットに含まれるストリームだけを止め、ほかのストリームは進み続ける(フローの分離) - [RFC 2475: An Architecture for Differentiated Services](https://www.rfc-editor.org/rfc/rfc2475) · IETF · シェーピングはパケットを遅らせ、ポリシングは超過分を破棄するという定義 - [RFC 3168: The Addition of Explicit Congestion Notification (ECN) to IP](https://www.rfc-editor.org/rfc/rfc3168) · IETF · ECN:パケットを破棄せずに輻輳を知らせる - [Smart Queue Management](https://www.bufferbloat.net/projects/cerowrt/wiki/Smart_Queue_Management/) · Bufferbloat.net · ルーターのSQM:フローごとのスケジューリング・AQM・シェーピングで、キューあふれとバッファブロートを減らす - [RFC 1191: Path MTU discovery](https://www.rfc-editor.org/rfc/rfc1191) · IETF · サイズ超過を知らせるICMP(タイプ3コード4)で経路MTUを探る - [RFC 1812: Requirements for IP Version 4 Routers](https://www.rfc-editor.org/rfc/rfc1812) · IETF · ルーターはICMPエラーメッセージの送信レートを制限できる(途中の1区間だけパケットロスがあるように見えるmtrの結果) - [RFC 2991: Multipath Issues in Unicast and Multicast Next-Hop Selection](https://www.rfc-editor.org/rfc/rfc2991) · IETF · マルチパスではping・tracerouteのような診断の結果を信頼しにくいことと、フローのハッシュで経路を固定する方式 - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_recovery(デフォルト0x1でRACK、6.17以降はRACKが唯一のロス検知なので0にしても効果なし)、tcp_early_retrans(デフォルト3、0ならTLPは無効)、tcp_sack・tcp_dsack・tcp_timestamps(デフォルトで有効)、tcp_thin_linear_timeouts(in-flightのパケットが4個未満、最大6回まで線形)、tcp_rto_max_ms、tcp_mtu_probing・tcp_base_mss、tcp_rto_min_us、tcp_retries2(デフォルト15、約924.6秒)、tcp_syn_linear_timeouts、tcp_notsent_lowat - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpOutSegsには再送が含まれない、TCPLossProbes・TCPLossProbeRecovery・TCPLostRetransmit・TCPSpuriousRTOs・TCPDSACKRecv・TCPSynRetransの意味 - [net/ipv4/proc.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/proc.c?h=v6.12) · Linux kernel · nstatに表示されるカウンター名(RetransSegs、TCPTimeouts、TCPLossProbes、TCPSpuriousRTOs、TCPDSACKRecvなど) - [Thin-streams and TCP](https://docs.kernel.org/networking/tcp-thin.html) · Linux kernel · ゲームのようなthin streamでは高速再送がうまく働かず長いタイムアウトに頼ることになる、TCP_THIN_LINEAR_TIMEOUTS - [include/net/tcp.h](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp.h?h=v6.12) · Linux kernel · TCP_RTO_MIN 200ms、TCP_RTO_MAX 120秒、TCP_TIMEOUT_INIT 1秒、遅延ACK 40〜200ms(TCP_DELACK_MIN・MAX)、TCP_BASE_MSS 1,024、thin streamの基準(in-flightのパケットが4個未満) - [net/ipv4/tcp_input.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_input.c?h=v6.12) · Linux kernel · RTO = SRTT + rttvar(RTOの最小値以上)、RACKはSACKを使う接続にだけ適用、回復中の輻輳ウィンドウの縮小にPRRを使用 - [net/ipv4/tcp_output.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_output.c?h=v6.12) · Linux kernel · TLPはSACKを使う接続にだけ適用、待ち時間は2・RTT、確認応答のないパケットが1つだけならRTOの最小値を加える - [net/ipv4/tcp_timer.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_timer.c?h=v6.12) · Linux kernel · RTOがtcp_retries1回続くとブラックホールを検知したとみなしてMTU探索を開始、thin stream・SYNの線形タイムアウト - [net/ipv4/tcp_recovery.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_recovery.c?h=v6.12) · Linux kernel · RACKの猶予時間 = min(min_RTT/4 × 段階, SRTT)、最小RTTより早く確認応答が来た再送は基準から除外 - [net/ipv4/tcp_ipv4.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_ipv4.c?h=v6.12) · Linux kernel · デフォルト値の初期化:tcp_early_retrans 3、tcp_recovery RACK、tcp_syn_linear_timeouts 4、tcp_base_mss 1,024(tcp_mtu_probingは個別に設定していないため0) - [net/ipv4/tcp_bbr.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/tcp_bbr.c?h=v6.12) · Linux kernel · BBRのpacing_rate = pacing_gain × ボトルネック帯域幅(ペーシングで均等に送る) - [tcp: use RACK to detect losses](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f41b1c58a32537542f14c1150099131613a5e8a) · Linux kernel · RACKとtcp_recoveryの導入(Linux 4.4)、当初は従来方式の補助として動作 - [tcp: disable RFC6675 loss detection](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b38a51fec1c1f693f03b1aa19d0622123634d4b7) · Linux kernel · RACKをデフォルトのロス検知に変更(2018年、Linux 4.18) - [tcp: remove obsolete and unused RFC3517/RFC6675 loss recovery code](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1c120191dcec510cc17d587ece48a7ae875a90c5) · Linux kernel · RFC6675のロス回復コードを削除(Linux 6.17)、RACK-TLPが2018年からデフォルトだったという説明 - [tcp: make the first N SYN RTO backoffs linear](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ccce324dabfe2143519daf50ed8b1ef1d0c542f7) · Linux kernel · SYNのRTOの最初の4回のバックオフを2倍にしない(最初のRTO 1秒のあと、さらに4回1秒ずつ、その後2、4秒…、Linux 6.5) - [tcp: add sysctl_tcp_rto_min_us](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f086edef71be7174a16c1ed67ac65a085cda28b1) · Linux kernel · サーバー全体のRTO最小値tcp_rto_min_us(Linux 6.11) - [tcp: add the ability to control max RTO](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=54a378f43425085d0684679d99735696b69165bc) · Linux kernel · TCP_RTO_MAX_MSソケットオプション、1〜120秒(Linux 6.15) - [tcp: support TCP_RTO_MIN_US for set/getsockopt use](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f38805c5d26fe4af97837c10d58074a7496638bf) · Linux kernel · 接続ごとにRTOの最小値を決めるTCP_RTO_MIN_USソケットオプション(Linux 6.15) - [Android common kernels](https://source.android.com/docs/core/architecture/kernel/android-common) · Android (Google) · サポート中のAndroid共通カーネルは5.10以上(RACKがデフォルトになった4.18より新しいカーネル) - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_NODELAY(Nagleアルゴリズムを無効化)、TCP_USER_TIMEOUT(再送のタイミングは変えず、あきらめるタイミングだけを決める)、TCP_KEEPIDLE、TCP_INFO - [ip-route(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ip-route.8.html) · iproute2 · 経路ごとのrto_minオプション - [tc-fq(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-fq.8.html) · iproute2 · fqキューによる接続ごとのペーシングとSO_MAX_PACING_RATE - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · TCPMSS --clamp-mss-to-pmtu:ICMPが遮断された区間を、SYNのMSSを調整して回避 - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · ss -iのrto(ms)、backoff(指数バックオフの回数)、rtt/rttvar、cwnd - [misc/ss.c](https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/misc/ss.c?h=v6.12.0) · iproute2 · ss -tiのretrans:現在/累計、lost、reordering、bytes_sent・bytes_retransの出力 - [nstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/nstat.8.html) · iproute2 · nstatはデフォルトで、前回の実行以降の増分を表示 - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · 再送ごとにアドレス・ポート・状態を1行ずつ表示、-cはフローごとに集計、-lはTLPの試行も含める - [Interface statistics](https://docs.kernel.org/networking/statistics.html) · Linux kernel · ip -s -s linkで詳細なエラーまで確認、rx_missed_errors・rx_crc_errorsの意味、ethtool -Sのドライバーごとの統計 - [drivers/net/ethernet/intel/igb/igb_ethtool.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/intel/igb/igb_ethtool.c?h=v6.12) · Linux kernel · ドライバーごとのカウンター名の例:rx_missed_errors、rx_no_buffer_count、rx_crc_errors - [Ethtool counters](https://docs.kernel.org/networking/device_drivers/ethernet/mellanox/mlx5/counters.html) · Linux kernel · mlx5のrx_out_of_buffer・rx_discards_phy - [net/core/net-procfs.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/net-procfs.c?h=v6.12) · Linux kernel · /proc/net/softnet_statはCPUごとに1行、16進数、2列目がdropped、3列目がtime_squeeze - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · bw_in・bw_out・pps・conntrack・linklocal_allowance_exceededの意味、CloudWatchで見るにはCloudWatchエージェントのインストールが必要 - [High-Resolution Measurement of Data Center Microbursts](https://research.facebook.com/publications/high-resolution-measurement-of-data-center-microbursts/) · Meta · バーストの大部分は数十µs以内に終わるため、分単位の平均利用率ではドロップの原因が見えない(IMC 2017) - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · -T(--tcp)でTCP SYNを使い、-P(--port)で宛先ポートを指定 - [pathping](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/pathping) · Microsoft · 何度も送信して区間ごとのパケットロス・遅延を計算する、Windowsの経路診断コマンド - [Display Filter Reference: Transmission Control Protocol](https://www.wireshark.org/docs/dfref/t/tcp.html) · Wireshark · tcp.analysis.retransmission・fast_retransmission・spurious_retransmission・duplicate_ack・lost_segment・zero_windowの表示フィルター - [7.5. TCP Analysis](https://www.wireshark.org/docs/wsug_html_chunked/ChAdvTCPAnalysis.html) · Wireshark · 再送・高速再送・不要な再送・ZeroWindowの判定条件 - [Network-Related Performance Counters](https://learn.microsoft.com/en-us/windows-server/networking/technologies/network-subsystem/net-sub-performance-counters) · Microsoft · TCPv4 Segments Retransmitted/sec·Segments Sent/sec, Network Interface Packets Received Discarded - [TCP/IP connectivity issues troubleshooting](https://learn.microsoft.com/en-us/troubleshoot/windows-client/networking/tcp-ip-connectivity-issues-troubleshooting) · Microsoft · netsh int tcp show globalでTCPのグローバル設定(Max SYN Retransmissions)を確認、両端での同時キャプチャで途中のパケットロスを確認 - [Packet Monitor (Pktmon)](https://learn.microsoft.com/en-us/windows-server/networking/technologies/pktmon/pktmon) · Microsoft · Windowsのネットワークスタックの複数の地点で、パケットが破棄された場所と理由を表示する組み込みツール - [Pktmon command formatting](https://learn.microsoft.com/en-us/windows-server/networking/technologies/pktmon/pktmon-syntax) · Microsoft · Windows 10・Windows Server 2019(1809以降)にpktmon.exeとして組み込み - [TCP improvements in the Windows network stack (IETF 98 TCPM)](https://datatracker.ietf.org/meeting/98/materials/slides-98-tcpm-tcp-improvements-in-windows-01) · Microsoft · Windows 10 Anniversary Update(1607)・Server 2016でTLPとRACKをデフォルトで有効化(RTTが10msを超える接続)、パケットが1つだけ残っているとき、TLPは遅延ACKの200msを考慮 - [Algorithmic improvements boost TCP performance on the Internet](https://techcommunity.microsoft.com/blog/networkingblog/algorithmic-improvements-boost-tcp-performance-on-the-internet/2347061) · Microsoft · TLPはWindows Server 2016からデフォルト、再送したパケットのロスまで回復する新しいRACKはServer 2022に搭載、PRRはWindows 10 1903からデフォルト - [TcpMaxConnectRetransmissions](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc938209(v=technet.10)) · Microsoft · 以前のWindowsのSYN再送:3秒から2倍ずつ2回 ### #owners - [RFC 4787: Network Address Translation (NAT) Behavioral Requirements for Unicast UDP](https://www.rfc-editor.org/rfc/rfc4787) · IETF · NATのマッピングは内側から出ていくパケットで必ず更新され(REQ-6)、外から入ってくるパケットによる更新は任意(UDPの場合)。そのためハートビートはクライアントが送る - [RFC 5382: NAT Behavioral Requirements for TCP](https://www.rfc-editor.org/rfc/rfc5382) · IETF · NATはアイドル状態のTCPセッションを削除することがあり、推奨されるアイドルタイムアウトは2時間4分以上(機器によって設定が異なる場合がある) - [Amazon EC2 security group connection tracking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-connection-tracking.html) · AWS · セキュリティグループの接続追跡のTCPアイドルタイムアウト(Nitro v6インスタンスタイプは350秒、その他は5日、60秒〜5日の範囲で調整可能)、5分より短い間隔のkeepaliveを推奨、NLBを経由するTCPは350秒 - [Control subnet traffic with network access control lists](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html) · AWS · ネットワークACLはステートレス(接続追跡なし)なので、応答トラフィックもルールで別途許可する必要がある - [Monitor network performance for ENA settings on your EC2 instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) · AWS · インスタンスの接続追跡・秒間パケット数の上限超過カウンター(conntrack_allowance_exceeded、pps_allowance_exceeded) - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · listenのbacklog引数が実際のキューのサイズで、somaxconnより大きければその値に切り詰められる(5.4からデフォルト4096) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · somaxconn(listenのbacklogの上限)、tcp_max_syn_backlog、tcp_syncookies(デフォルト1、SYNキューがあふれたときの備え)、tcp_keepalive_time(デフォルト2時間) - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · acceptキューが満杯になるとSYNを破棄し、TcpExtListenOverflows・TcpExtListenDropsが増える - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · nf_conntrack_countとnf_conntrack_maxからconntrackの使用率を計算 - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · cpu.statのnr_throttled:コンテナのCPU上限によってスロットリングされた回数 - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · /proc/statのsteal:仮想化環境で他のOSがCPUを使っていた時間 - [RFC 2992: Analysis of an Equal-Cost Multi-Path Algorithm](https://www.rfc-editor.org/rfc/rfc2992) · IETF · ECMPは、フローを識別するヘッダーフィールドのハッシュで経路を選ぶ(同じフローは同じ経路、フローが異なれば別の経路になりうる) - [mtr(8) manual page source](https://raw.githubusercontent.com/traviscross/mtr/master/man/mtr.8.in) · mtr · ゲームと同じプロトコル・ポートで経路を測定(-T、-u、-P) ### #l-server-proc - [VALORANT's 128-Tick Servers](https://www.riotgames.com/en/news/valorants-128-tick-servers) · Riot Games · ティックバジェット:128ティックなら1フレームを7.8125ms以内に終える必要がある、フレーム時間をサブシステムごとに計測し、バジェットを分けて管理 - [Introducing Time Dilation (TiDi)](https://www.eveonline.com/news/view/introducing-time-dilation-tidi) · CCP Games · EVE Onlineの物理シミュレーションは1秒に1回更新、過負荷時にはゲーム内の時計を遅らせ、時間に連動する負荷を比例して減らす - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · Time Dilationの下限は10%、n人の行動をn人に知らせるO(n²)の送信が大規模戦闘の限界要因 - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · ティック実験のモデル:固定間隔の進行が遅れると追いつくためのステップをまとめて実行し(早送り)、上限を超えた時間は切り捨てるためゲーム内の時間が遅くなる(スローモーション) - [Comparing Interest Management Algorithms for Massively Multiplayer Games](https://www.sable.mcgill.ca/~clump/papers/boulanger-06-comparing.pdf) · ACM · NetGames 2006の論文(著者による公開版)。すべてのペアの距離を比較する方法は人数が増えると処理しきれず、グリッドに分ければ周囲のセルだけを確認すればよい - [Replication Graph in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/replication-graph-in-unreal-engine) · Epic Games · ワールドをグリッドに分け、セルごとのリストで送信先を選べば、人数・アクターが多くてもサーバーのCPUを節約できる - [Amdahl's Law in the Multicore Era](https://research.cs.wisc.edu/multifacet/papers/ieeecomputer08_amdahl_multicore.pdf) · IEEE · ロック実験のスループット上限:一度に1つしか実行できない部分の割合が1−fなら、高速化は1/(1−f)を超えられない - [Runtime locking correctness validator](https://docs.kernel.org/locking/lockdep-design.html) · Linux kernel · ロック実験のデッドロック:2つのロックを互いに逆の順序で取ると、循環待ちでデッドロック - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · ロック実験のウォッチドッグ:デッドロック状態をliveness probeで検出して再起動、デフォルトでは10秒ごとに検査し、3回連続で失敗すると再起動(約30秒) - [ASP.NET Core Best Practices](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/best-practices) · Microsoft · 同期呼び出し:データアクセス・I/Oは非同期で呼び出す、ブロッキング呼び出しはスレッドプールの枯渇と応答の遅延を招く ### #l-infra - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · カスケード障害:遅いバックエンドが前段のスレッド・リソースを占有し、再試行・ヘルスチェックの失敗・キャッシュが空の状態での再起動によって障害が広がる過程と対策 - [Circuit Breaker Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) · Microsoft Azure · サーキットブレーカーのClosed・Open・Half-Openの状態と失敗回数のしきい値、タイムアウトが長いと遮断されるまでスレッドが拘束される - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · Microsoft Azure · 機能・呼び出し先ごとにリソースを分離し、1か所の障害が広がらないようにする - [Timeouts, retries, and backoff with jitter](https://d1.awsstatic.com/builderslibrary/pdfs/timeouts-retries-and-backoff-with-jitter.pdf) · AWS · Amazon Builders' Library。タイムアウトでリソースを解放し、再試行は回数を制限してジッターを加える - [The Unique Architecture behind Amazon Games’ Seamless MMO New World](https://aws.amazon.com/blogs/gametech/the-unique-architecture-behind-amazon-games-seamless-mmo-new-world/) · AWS · MMOのサーバー構成の例:入口サーバー、グリッドごとのシミュレーションサーバー(ハブ)、セッション用の共有サーバープール、状態を保存するDB - [Working with DB instance read replicas](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html) · AWS · 構成図実験のレプリケーション遅延:リードレプリカは非同期で更新されるため、古いデータを読むことがある - [Site Reliability Engineering, Chapter 20: Load Balancing in the Datacenter](https://sre.google/sre-book/load-balancing-datacenter/) · Google · デプロイ・再起動:lame duck状態にして新しいリクエストを他へ回してから終了、再起動直後にウォームアップ - [Amazon EC2 Auto Scaling lifecycle hooks](https://docs.aws.amazon.com/autoscaling/ec2/userguide/lifecycle-hooks.html) · AWS · スケールアウト・スケールイン時にインスタンスを待機状態にして、準備・後処理を済ませる(デフォルトで最大1時間) - [systemd.service(5) — Linux manual page](https://man7.org/linux/man-pages/man5/systemd.service.5.html) · systemd · 構成図実験のウォッチドッグ:生存を知らせる信号が途絶えたサービスを終了し、自動で再起動 ## グラフの形 - **周期的なスパイク** (`periodic`): 普段は低く、数秒・数分ごとや毎正時のように同じ間隔で跳ね上がります。 - **不定期なスパイク** (`random`): 決まった間隔なく不規則に跳ね上がり、すぐに戻ります。 - **ある時点から階段状に上昇** (`step`): アップデート・設定変更・経路変更など特定の時刻を境に一段上がり、そのまま戻りません。 - **徐々に上昇** (`ramp`): 数時間・数日かけて少しずつ上がります。稼働時間とともに大きくなります。 - **徐々に上昇して急落** (`sawtooth`): ゆっくり上がっては、再起動・クリーンアップの瞬間にストンと落ちるのを繰り返します。 - **特定の時間帯だけ高い** (`peak`): 夜のピークのように、一日のうち同じ時間帯にだけ山なりに上がります。 - **人数・負荷に連動して上昇** (`load`): 同時接続数や一か所に集まった人数が増えると、それを上回る勢いで上がります。 - **上限で頭打ち** (`ceiling`): スループット・接続数がある値に達するとそれ以上伸びず、そこから待ち・エラーが増えます。 - **最初から常に高い** (`high`): 跳ねることなく、ずっと高い値にとどまります。距離・経路・設計など、構造に起因する場合です。 - **一部だけ高い** (`outlier`): 大部分は正常で、特定のユーザー・地域・ISP・端末だけが高くなります。 - **途切れた後にまとめて到着** (`gap`): しばらく受信量が0になった後、一気にまとめて届きます。 - **接続が一斉に切れる** (`drop`): 接続数がガクッと落ちるか、切断数が一瞬で跳ね上がります。 - **接続直後・メンテ明けに急増** (`surge`): サーバーのオープンやイベント開始の直後に大きく跳ね上がり、徐々に落ち着きます。 ## 状況別の手順 ### アップデート後のラグ 特定のアップデート・デプロイの後からラグ報告が増えたとき。「今回のアップデートからおかしい」という報告が集まったときや、グラフがある時刻から階段状に上がって高止まりしている場合に使う。 1. **開始時刻を確定し、その前後の変更をすべて集める**: 報告が最初に集中した時刻と、グラフが階段状に上がった時刻を特定し、その前後に入った変更を漏れなく書き出す。クライアントのアップデート、サーバーのデプロイ、設定変更、DBのスキーマ変更(DDL)と再起動、ネットワーク・ファイアウォールの作業、インフラの入れ替え(インスタンスタイプ・カーネル・ドライバー)をあわせて確認する。デプロイのたびに監視ツールの注釈(annotation)機能ですべてのグラフに縦線を残しておけば、このステップはすぐに終わる。ゲームのアップデートとインフラ作業が同じメンテナンスで行われていたなら、両方を候補に残す。最初に呼ぶ担当:変更を入れたゲーム開発チームとインフラチームの両方。(原因:in-deploy, so-os-update, db-ddl-lock, db-plan-flip, db-cold-cache) 2. **範囲を切り分ける:ビルド・端末・サーバー・地域**: 異常がどの軸に偏っているかを確認する。新しいビルドのユーザーだけが悪ければクライアント、特定のOS・グラフィックカード・端末だけならクライアントの性能やドライバー、特定のサーバー・チャンネル・ゾーンだけならサーバー、特定の国・通信事業者だけならネットワーク経路、全員が同時に悪ければ共有リソース(DB・ロードバランサー・ゲートウェイ)か直前のサーバーデプロイを、まず疑う。クライアントのテレメトリにビルド番号があれば、旧ビルドと新ビルドのPing・FPS・フレームスパイク・切断回数を並べて比較する。Pingは変わらずFPSだけが悪化したなら、ネットワークよりもクライアントの性能の問題だ。最初に呼ぶ担当:ビルド・端末に偏るならゲーム開発チーム(クライアント)。サーバー・チャンネルに偏るなら、ホストのメトリクスが正常な場合はゲーム開発チーム(サーバー)、異常な場合はインフラチーム(サーバー機器・OS)。国・通信事業者に偏るならインフラチーム(ネットワーク)。(原因:cg-hitch, cg-sync-load, co-vram, cg-crash) 3. **新旧バージョンを同じ時間帯で比較する**: デプロイ前後の比較だけでは、曜日・時間帯・イベントによる変化が混ざり、判断がぶれる。可能なら新バージョンを一部のサーバー(カナリア)に先に入れ、同じ時間帯の旧バージョンのサーバー(対照群)と、ティック時間のp50・p99、ティック超過回数、CPU、メモリ、エラー率を並べて比較する。すでに全体にデプロイ済みなら、先週の同じ曜日・同じ時間帯と比較する。サーバー全体の平均だけでは一部のサーバー・ゾーンの問題が埋もれるので、サーバー・ゾーン別に分けて確認する。最初に呼ぶ担当:ゲーム開発チーム(サーバー)。(原因:sp-tick-overrun, mem-alloc, mem-leak, sp-broadcast) 4. **トラフィックのフィンガープリントを前後で比較する**: サーバーのコードを知らなくても、ネットワーク側から見える値で、アップデートがトラフィックの形を変えたかどうかを確認する。ユーザーあたりの秒間パケット数(pps)とバイト数、平均・最大パケットサイズ、接続数、ティックごとに一気に送り出される送信バーストの大きさを前後で比較する。UDPパケットが経路MTU(通常1,500バイト)を超え始めたなら、IPフラグメンテーションが起きている。フラグメントが一つ失われるだけでパケット全体が失われ、フラグメントを最初から破棄するNAT・ファイアウォールもある。途中でMTUの小さい区間(トンネル・VPN)を通るユーザーでは、大きなパケットだけが消える。ppsが増えたなら、クラウドインスタンスのPPS上限や、ファイアウォール・DDoS対策機器の処理上限に達していないかを確認する。最初に呼ぶ担当:フィンガープリントが変わっていれば証拠を添えてゲーム開発チーム(サーバー)、変わっていないのにパケットロス・再送だけ増えたならインフラチーム(ネットワーク)。(原因:sp-patch-traffic, sk-fragment, rt-mtu, nic-cloud-pps, rt-appliance-pps, rt-burst) 5. **DBクエリの種類と回数を前後で比較する**: DBの遅延が増えたなら、まずクエリ数(QPS)も一緒に増えたかを確認する。PostgreSQLのpg_stat_statementsやMySQL Performance Schemaのdigestサマリーは、値だけが異なるクエリをひとまとめにして実行回数と合計時間を集計してくれる。アップデート前後の上位クエリ一覧を比較すれば、新たに現れたクエリ、回数が数倍に増えたクエリ(N+1)、インデックスを使わずにテーブル全体を読むクエリ(MySQLではSUM_NO_INDEX_USEDカラム)が浮かび上がる。最初に呼ぶ担当:QPSやクエリの形が変わっていればゲーム開発チーム(サーバー)、クエリは同じで遅延だけ増えたならインフラチーム(DB:実行計画・IOPS・ロック)。(原因:db-no-index, db-login-storm, db-plan-flip, db-cache-stampede) 6. **ホストとサーバープロセスのメトリクスで階層を切り分ける**: コードに頼らず、OSから見える値でサーバープロセスの内側とホストを切り分ける。サーバーソケットの受信キュー(Recv-Q)がたまっていれば、サーバープロセスが時間どおりに読み出せていない(ティックの停止・GC・ロック)。一つのスレッドだけが100%ならシングルスレッドのボトルネック、GCログの停止時間が延びていればメモリの使い方が変わったということだ。ログレベルを上げたままデプロイしてログ書き込みが増えていないかも確認する。逆に、CPUスチール・スロットリング・NICのドロップが増えていれば、同じ時刻に変わったインフラ(インスタンスタイプ・カーネル・コンテナの上限)を確認する。最初に呼ぶ担当:プロセス内側のシグナルならゲーム開発チーム(サーバー)、ホストのシグナルならインフラチーム(サーバー機器・OS)。(原因:mem-gc, sp-hotzone, dk-sync-log, so-cpu-quota, so-steal, so-os-update) 7. **元に戻して確定し、記録を残す**: 最も有力な変更を一部のサーバーや一部のユーザーに対してだけ元に戻す(ロールバック、フィーチャーフラグをオフ)か、設定を以前の値に戻し、症状も一緒に消えるかを確認する。戻した側だけが改善すれば原因が確定する。戻す作業でも再起動とコールドキャッシュで一時的に重くなることがあるので、急ぎでなければ空いている時間帯に行う。結果は原因IDとともに障害記録に残し、パケットサイズ・クエリ数・ティック時間の上限を、次のアップデートのデプロイ前チェック項目に加える。最初に呼ぶ担当:変更を入れたチーム。(原因:in-deploy, db-cold-cache) ### 海外の国・地域の追加 サービス提供国を新たに開くとき、または新しいリージョン・データセンターを追加するとき。オープン前の点検と、「国内は問題ないのに新しい国のユーザーだけラグい」という報告の切り分けの両方に使う。 1. **現地の通信事業者ごとの経路品質をオープン前に測る**: 対象国の主要な通信事業者(ASN)ごとに、ゲームサーバーの候補地までの往復時間(RTT)の分布、ジッター、パケットロスを測る。平均値一つでは通信事業者ごとの差が埋もれるので、通信事業者別の中央値と95パーセンタイルを、夜のピークと深夜に分けて確認する。公開測定網のRIPE Atlasでは、国・ASNを指定して世界中のプローブからping・tracerouteを送れる。候補リージョンに一時的なVMを立てて測ってもよい。途中の機器がICMP応答をレート制限することもあるので、可能ならゲームと同じプロトコル・ポートでも測る。特定の通信事業者だけが際立って遠い都市を経由していれば、ピアリング・経路の問題だ。通信事業者は遅延よりもコストの低い経路を選ぶため、近い場所でも遠回りすることがある。最初に呼ぶ担当:インフラチーム(ネットワーク)、経路が通信事業者側の問題なら外部(通信事業者・IX)。(原因:isp-distance, isp-routing, isp-peak, isp-cable) 2. **測定値を、ゲーム設計が耐えられる上限と比較する**: 測ったRTT・ジッターを、ゲームの判定の受付時間(回避・パリィなどの反応時間)、ラグコンペンセーションの上限、補間バッファの長さ、入力バッファのサイズと比較する。たとえばパリィの判定が0.2秒なら、往復遅延と補間バッファを足した値がそれより長い通信事業者のユーザーは、タイミングどおりに反応しても間に合わない。ラグコンペンセーションを広げて合わせると、今度は攻撃を受ける側から「壁の裏にいたのに当たった」という報告が増える。上限を超える通信事業者が多ければ、インフラチームはリージョン・エッジPoPをより近くに置く案を、ゲーム開発チームは判定・補間・ラグコンペンセーションの値を検討する。白書の「同期方式」の章が基準表になる。最初に呼ぶ担当:ゲーム開発チーム(サーバー・クライアント:設計上の上限)、インフラチーム(ネットワーク:リージョン・PoPの位置)。(原因:sy-short-window, sy-no-lagcomp, sy-lagcomp-overreach, cg-no-buffer) 3. **MTUとUDPが通るかを確認する**: 現地のネットワークで、ゲームの最大パケットが欠けずに通るかを確認する。フラグメント化禁止(DF)フラグを立てたpingをサイズを変えながら送って経路MTUを測り、PPPoE・トンネル・モバイル回線のように1,500バイトより小さい区間がないかを確認する。UDPのようなデータグラム転送の標準(RFC 8899)は、IPv4でほとんどの経路を通過できる基本サイズとして1,200バイトを推奨している。ゲームの最大パケットがこれより大きければ、小さくするか分割して送る方法をゲーム開発チームと決める。公衆Wi-Fi・社内ネットワーク・一部の通信事業者でUDPやゲームのポートがブロックされたり速度制限されたりしないかも確認し、ブロックされたときの代替経路(TCP・443番ポート)があるかを確認する。最初に呼ぶ担当:インフラチーム(ネットワーク)とゲーム開発チーム(サーバー:パケットサイズ)。(原因:dc-mtu, rt-mtu, sk-fragment, isp-udp-block, hn-captive, isp-shaping) 4. **NAT・CGNATのアイドルタイムアウトを測り、ハートビート間隔を合わせる**: 現地の家庭用ルーターとモバイル回線(CGNAT)が、アイドル状態のUDP接続のマッピングをどれくらいで消すかを測る。試験ごとに、テスト端末からサーバーへパケットを一つ送ってマッピングを作り、その後は端末から何も送らず、決めておいた時間(30秒、60秒、120秒…)が過ぎてからサーバーが端末へパケットを送るようにする。端末がそのパケットを受け取れなくなり始める時間が、そのネットワークのアイドルタイムアウトだ。標準(RFC 4787)は、UDPマッピングを2分未満で期限切れにしてはならず、デフォルトは5分以上を推奨しているが、機器によって値は大きく異なり、もっと短く消す機器もある。マッピングが確実に更新されるのは端末から出ていくパケットだけなので、ハートビートはクライアントから送る。その間隔が、測った値とロードバランサー・クラウドのセキュリティグループのアイドルタイムアウトのうち最も短い値の半分以下になっているかを確認する。最初に呼ぶ担当:ゲーム開発チーム(クライアント:ハートビート間隔、サーバー:タイムアウト値)、インフラチーム(ロードバランサー・セキュリティグループの設定)。(原因:hn-nat, isp-cgnat, rt-mapping, dc-lb-idle, dc-cloud-conntrack) 5. **現地で経由する外部サービスとセキュリティ機器を確認する**: 現地のプラットフォームのログイン・決済・本人認証が本来の速さで応答するか、現地のDNSでログインサーバー・アップデートサーバーのアドレスが正しく解決されるか、CDNがその国に近い拠点からアップデートデータを配信しているかを確認する。DDoS対策・ファイアウォールの国別ブロックルールやレート制限に新しい国のIPアドレス帯が引っかからないかを確認し、特に複数の加入者が一つのIPを共有するCGNATのアドレス帯がまとめてブロックされないかを確認する。最初に呼ぶ担当:インフラチーム(セキュリティ機器・DNS・CDN)、外部(プラットフォーム・決済事業者・通信事業者)。(原因:in-external, isp-dns, dc-ddos, isp-cgnat) 6. **オープン後は国・ASN別に分けて確認する**: 接続ログ・ロードバランサーログのクライアントIPに国とASNを付与し、国・通信事業者別にRTT、再送、切断の回数と理由(ハートビートタイムアウト・RST・サーバーからのキック)を確認する。MaxMind GeoLite ASNのような無料データベースでIPをASNと組織名に変換できる。現地の個人情報保護規制に合わせて、IPは/24やASN単位に丸めて保管する。一つのASNだけに集中していればその通信事業者の経路(インフラチーム・外部)、新しい国全体が悪ければ距離と設計上の上限(インフラチーム・ゲーム開発チーム)、夜だけ悪化するならピアリングの輻輳を、まず疑う。一部のユーザーだけ常にPingが高ければ、GeoIPの誤り・VPN・パーティリーダー基準の割り当てによって遠いリージョンに割り当てられていないかを、ゲーム開発チーム(サーバー)と一緒に確認する。外形監視は正常なのにユーザーだけが悪ければ、ユーザー環境かクライアント側の問題だ。(原因:isp-peak, isp-routing, rt-queue-drop, pt-isp-validation, in-region-match) 7. **遠い地域のユーザーがほかのユーザーに与える影響を確認する**: 遠くから接続するユーザーが増えると、その人の画面が悪くなるだけでは済まない。遅延の大きい人の入力がまとめて届くため、ほかの人の画面ではそのキャラクターだけが早送りで動き、サーバーの速度・クールタイムのチェックに引っかかって引き戻しやスキルの拒否が起きる。パーティギミックでは、遅延の大きい一人の反応の遅れがパーティ全体の失敗につながり、ロックステップ方式では最も遅い人を全員が待つ。新しい国のオープン後に既存ユーザーからの「特定のキャラだけおかしく見える」という報告が増えていないかを確認し、入力バッファ・検証の許容値・マッチング地域の分離をゲーム開発チームと決める。最初に呼ぶ担当:ゲーム開発チーム(サーバー)。(原因:pt-slow-burst, pt-isp-validation, pt-raid-member, sy-lockstep) ## 実際の障害事例 ### eve-hedgp-2014 · CCP Games 2014: EVE OnlineのHED-GP大規模艦隊戦でのサーバー過負荷 - 何が起きたか:2014年1月のポストモーテムで取り上げられたHED-GP星系の大規模艦隊戦で、サーバーが深刻な過負荷に陥った。Time Dilation(過負荷時にゲーム内の時間の進みを遅くする機能)が下限の10%に達し、戦場全体がスローモーションになった後も負荷はたまり続けた。モジュールの停止や繰り返し作動を処理する作業の遅れ(Dogma Lateness)は、最大でゲーム内時間193秒、実時間で約32分に達した。規模がほぼ同じだった2013年7月の6VDTの戦闘では、最大42秒(実時間で約7分)だった。 - 原因:CCPは、性能分析ツール自体が負荷を増やすためこうした状況では動かしておらず、確実なことは言えないと前置きしたうえで、2つを有力な原因に挙げた。1つは、戦闘が長引くにつれて処理しきれない負荷がたまり続けたことだ。もう1つはドローンの使用増加で、戦闘中に展開されたドローンの数(重複を除く)は6VDTが21,123機、HED-GPが38,852機と84%多かった。一人の行動を、それが見える全員に知らせる送信は人数の二乗(O(n²))で増えるが、ドローンは1回の攻撃で発生するメッセージがさらに多い。ドローンが攻撃対象を選ぶコードも、同じ戦場で攻撃可能な対象を頻繁に総当たりで調べるため、コストがn²に近い形で増える。 - 教訓:人が集中した一つのエリアの処理量が限界を超えると、そのエリア全体がスローモーションになり、戦闘が長引くほど処理の遅れがたまって入力遅延が大きくなる。確認すべきシグナルは、そのエリアを担当するサーバー(ノード)のティック時間・滞留した作業量と、人数・オブジェクト数だ。ほかのエリアは正常なのが特徴だ。主担当はゲーム開発チーム(サーバー)で、一つの行動を通知する相手の範囲と、AIの対象探索コストが改修ポイントになる。ゲーム内の時間を遅くする設計で過負荷そのものはなくせないが、全員が同じ速さで遅くなるので、一部の行動だけが際限なく後回しになることを防げる。 - 関連する原因:sp-broadcast, sp-tick-overrun, sp-queue, sp-hotzone - 原文:[CCP Games](https://www.eveonline.com/news/view/what-a-hed-ache) ### riot-direct-2015 · Riot Games 2015: 遠回りしていたLeague of LegendsのトラフィックとRiot Direct - 何が起きたか:Riot Gamesが、インターネットがリアルタイムゲームに向いていない理由を解説した技術記事だ。League of Legendsのユーザーが報告した実際のトラフィックは、サンフランシスコからポートランドへまっすぐ向かうべきところをロサンゼルス、デンバー、シアトルを経由しており、直行なら14msで済むところが70msかかっていた。Riotは、ルーターがあふれてパケットが破棄されると、ほかのチャンピオンが画面上で飛び跳ねるように動き、投射物がワープしたように見えると説明した。 - 原因:Riotは経路とルーターを原因に挙げた。バックボーン事業者と通信事業者は、遅延が最も短い経路よりもコストが最も安い経路でトラフィックを流し、BGPで決まった経路が遠回りになれば経由するルーターの数も増える。ルーターの処理負荷は、パケットのサイズに関係なく個数に比例する。ゲームのパケットは55バイト前後なので、同じデータ量なら1,500バイトのパケットより個数が27倍になり、その分ルーターの入力バッファを早く埋める。Riotの説明によれば、過負荷になるとUDPパケットから先に破棄するルーターも多い。解決策としてRiotは、米国の主要なインターネット拠点10か所にルーターを置き、できるだけ多くの通信事業者と直接接続(ピアリング)する自社ネットワークRiot Directを構築した。第2部によると、Pingが80ms未満でプレイするユーザーの割合は9か月あまりで31%から50%に上がり、ゲームサーバーをシカゴに移すと一晩で80%になった。 - 教訓:同じ国の中でも特定の通信事業者のユーザーだけPingが際立って高ければ、経路を疑う。確認すべきシグナルは、通信事業者(ASN)別のRTT分布と、tracerouteに表れる経由都市だ。主担当はインフラチーム(ネットワーク)で、通信事業者との直接ピアリング、IX接続、サーバーの設置場所の選定で解決する。通信事業者側の経路ポリシーは、外部(通信事業者)と協議する事項だ。サーバーをユーザー分布の中心近くに移すだけでも効果が大きいことも、この事例は示している。 - 関連する原因:isp-routing, isp-distance, rt-queue-drop - 原文:[Riot Games](https://www.riotgames.com/en/news/fixing-internet-real-time-applications-part-i) ### riot-edge-2020 · Riot Games 2020: League of Legendsの欧州・ブラジルサーバーでのエッジホスト過負荷 - 何が起きたか:2020年2月末、League of LegendsのEUW・EUNE・BRサーバーで障害が何度も発生し、新たに始まるゲームの数が大きく減った。マッチング・ゲームサーバーなどのバックエンドサービスはどれも正常な状態だったが、流入するトラフィックがほとんどなかった。Riotは、不安定になりうるクラスターでトーナメントモード(Clash)を開催しないよう、日程を1週間延期した。ポストモーテムには、障害ごとの継続時間は書かれていない。 - 原因:3つの要素が重なった。あるサービスへのリクエストが誤った形で組み立てられており、特定の条件で失敗と再試行を繰り返したため、リクエストが急増した。コンテナシステムとOSバージョンの既知の相性問題でOS内部のメモリがリークしていたが、アップグレードはRiotのコンテナ環境全体の約60%でしか終わっておらず、欧州・ラテンアメリカのクラスターは作業中だった。インターネットからのトラフィックを受けてフィルタリングし、バックエンドへ渡すエッジコンテナは、同じシャード(サーバー群)の中では別々のホストに配置されていたが、異なるシャード同士が同じホストに載るのは防げていなかった。そのため障害のたびに、少なくとも3つのシャードのエッジコンテナが1台のホストに集中していた。再試行の急増がそのホストに重なり、メモリリークがそのホストを停止させた。 - 教訓:バックエンドのサービスがどれも「正常だがトラフィックが来ない」という状態なら、その手前(エッジ・ゲートウェイ・ロードバランサー)を確認する。確認すべきシグナルは、ホスト別のインバウンド接続数の偏りと、特定リクエストの失敗・再試行の比率だ。主担当はゲーム開発チーム(サーバー:誤ったリクエストと再試行の方式)で、コンテナの配置ルール・OSのアップグレード・偏りのアラートはインフラチーム(サーバー機器・OS)が受け持つ。Riotはリクエストのコードを修正して再試行が急増しないように変え、シャード間の分散配置を実装するまでの間、偏りを検知するアラートを設けた。 - 関連する原因:in-gateway, in-cascade - 原文:[Riot Games](https://www.leagueoflegends.com/en-gb/news/riot-games/incident-report-recent-outages-in-europe-brazil/) ### riot-euw-2021 · Riot Games 2021: League of Legends EUWの5時間障害:補助的なDB一つでサーバー全体が停止 - 何が起きたか:2021年1月22日、League of LegendsのEUWサーバーが5時間強にわたって正常に動作しなかった。ログイン中のユーザー数とゲーム中のユーザー数のメトリクスが一斉に途切れ、2回の再起動の間は、ログインは増えてもゲームはほとんど始まらなかった。 - 原因:重要度の低い機能を担うDBのプライマリサーバーでハードウェア故障が起き、そのDBには予備サーバーへの自動フェイルオーバーが設定されていなかった。コネクションプールはDBごとに分かれていたが、すべてのプールが同じスレッドプールを使っており、故障したDBに送った処理が終わらないままスレッドを占有したため、システム全体で使えるスレッドが枯渇した。大量のアラートが鳴る中、最近受けた悪意あるネットワーク攻撃や別リージョンでのハードウェア作業を先に疑ったため、故障したDBのアラートに気づいたのは約1時間後だった。すべてのシステムが1つのJVMで動く構成だったため、再起動後の再接続の負荷の中でGCがプロセスを数秒ずつ止めると、メトリクス収集にも大きな空白が生じた。ログイン待機列も設定した上限を守らず、流入が安定しなかった。 - 教訓:重要ではないと考えていた補助的なDB一つでも、スレッドプールのような共有リソースを通じて全体を止めうる。確認すべきシグナルは、DB別の待機中リクエスト数とスレッドプール使用率、そしてログイン数に対してゲーム開始数が少なすぎる比率だ。担当はゲーム開発チーム(サーバー:スレッドプールの分離・タイムアウト)とインフラチーム(DB:自動フェイルオーバー)だ。アラートが大量に出ているときは最近経験した問題(攻撃など)から疑いがちなので、切り分けの順序(範囲 → 時点 → 階層)に沿って一つずつ除外する。再起動後は、ログイン待機列が設定どおりに流入を制限しているかもあわせて確認する。 - 関連する原因:sp-threadpool, db-failover, in-cascade, mem-gc - 原文:[Riot Games](https://www.riotgames.com/en/news/keeping-legacy-software-alive-case-study) ### roblox-2021 · Roblox 2021: Robloxの73時間障害:サービスディスカバリ(Consul)クラスターでの競合 - 何が起きたか:2021年10月28日午後(太平洋時間)、Consulサーバー1台の高いCPU負荷から始まり、16時35分に接続中のユーザー数が普段の半分に落ちた後、サービス全体が停止した。すべてのユーザーが再び入れるようになったのは10月31日16時45分で、障害発生から73時間かかった。Robloxは、毎日5千万人が利用していると公表している。 - 原因:Robloxはサービスディスカバリ(サービス同士が互いのアドレスを見つける仕組み)、ヘルスチェック、KVストアにHashiCorp Consulを使っており、1つのConsulクラスターが複数のワークロードをまとめて担っていた。根本原因は2つあった。1つ目は、数か月かけて適用範囲を広げてきたConsulの新しいstreaming機能を、障害の前日にトラフィックルーティングサービスでも有効にし、そのサービスのノード数を50%増やしたことだ。読み込みと書き込みがどちらも非常に多い負荷のもとで、この機能は1つの共有リソース(Goのチャネル)での競合を引き起こした。障害中に入れ替えた、コア数がより多いデュアルソケット(NUMA)サーバーでは、競合がさらに激しくなった。2つ目は、ConsulがRaftログの保存に使うBoltDBで空きページリスト(freelist)の管理が異常に遅くなり、16kB以下のデータを追加するたびに7.8MBをディスクに書き込んでいたことだ。普段は300ms未満だったKV書き込みレイテンシの中央値は2秒になり、遅いリーダーサーバーではTCPバッファが満杯になるゼロウィンドウも観測された。テレメトリもConsulに依存していたため、原因調査に必要なメトリクスまで一緒に失われた。 - 教訓:複数のサービスが共通して依存する基盤システム(サービスディスカバリ・設定ストア・認証)が遅くなると、すべての機能が一斉に止まる。確認すべきシグナルは、そのシステムの書き込みレイテンシ・リーダー交代・CPUと、障害直前の設定変更だ。担当はゲーム開発チーム(サーバー)とインフラチーム(サーバー機器・OS)の両方だ。障害時にもメトリクスを確認できるよう、監視は監視対象のシステムに依存しない形で分離しておく。復旧時はキャッシュが空で、一気に受け入れると再び崩れるおそれがあるため、RobloxはDNSで入場できるユーザーの割合を調整しながら約10%ずつ増やした。 - 関連する原因:in-cascade, sp-lock, rt-zero-window, db-cold-cache - 原文:[Roblox](https://about.roblox.com/newsroom/2022/01/roblox-return-to-service-10-28-10-31-2021) ### ffxiv-2021 · Square Enix 2021: FINAL FANTASY XIVの拡張パッケージ発売時の混雑とログイン待機列のエラー - 何が起きたか:2021年12月、拡張パッケージ『暁月のフィナーレ』(Endwalker)のアーリーアクセス開始から、各ワールドが極度に混雑した。ログイン待機列が長くなり、キャラクター選択画面からログインするときや待機列で待っている間にエラー2002が頻発した。一部のワールド・ゾーンのダウン(エラー3001)や待機列のタイムアウト(エラー4004)も起きた。12月11日のお知らせの時点でも、アーリーアクセス8日目で混雑が続いていた。 - 原因:エラー2002は2つのケースで発生する。1つは、論理データセンターごとの待機人数が17,000人を超えたときだ。待機列が長くなりすぎてログインサーバーがダウンするのを防ぐための上限で、このときはクライアントが完全に終了する。12月7日に開発用の予備機材をロビーサーバーに投入して上限を引き上げると、このエラーは減ったものの、待機列はかえって長くなった。もう1つは、待機中のユーザーの回線が不安定なときだ。待ち時間が長くなるにつれ、インターネット経路のパケットロスやWi-Fiの不安定さで接続が一瞬切れることが増えた。ロビーサーバーは数十秒から1分ほど再接続を待ち、その間に再接続できれば待機列の途中から再開できるが、それを過ぎると最後尾に並び直しになる。スクウェア・エニックスは、報告の大半がこのケースだとしている。半導体不足のため、ワールドをすぐに増やすこともできなかった。 - 教訓:待機列が長くなるほど、待機中のユーザー回線で起きる瞬断が接続エラーに変わる。同じ混雑の中でも、Wi-Fiや不安定な回線を使う人にエラーが集中する「一部のユーザーだけに起きる問題」になる。確認すべきシグナルは、待機列の長さ・待ち時間と、切断理由のうち待機中の切断が占める割合だ。主担当はゲーム開発チーム(サーバー:待機列の上限と再接続の猶予時間)で、ロビー・ワールドサーバーの増設はインフラチームが一緒に行う。再接続の猶予時間を十分に取れば、ユーザー回線の瞬断が待機順を失う事態に発展するのを減らせる。 - 関連する原因:in-login-queue, hn-wifi, rt-wireless - 原文:[Square Enix](https://na.finalfantasyxiv.com/lodestone/news/detail/6a94b30182b6d963994fdc0b789264ac9f24986f) ### cloudflare-2020 · Cloudflare 2020: Cloudflareのバックボーン設定ミスで一部都市のトラフィックが途絶 - 何が起きたか:多くのゲームがWeb・API・DDoS対策をCDN事業者に任せているため、ゲームも巻き込まれるタイプのインフラ障害だ。2020年7月17日21:12から21:39(UTC)までの27分間、Cloudflareネットワーク全体のトラフィックが約50%減少した。影響はバックボーンに接続された米国・欧州・ロシア・ブラジルの一部都市の拠点に限られ、ほかの拠点は正常だった。 - 原因:ニューアークとシカゴを結ぶバックボーン区間の障害でアトランタとワシントンを結ぶ区間が輻輳したため、エンジニアがアトランタのバックボーントラフィックを減らそうとルーターの設定を変更した。ポリシーの項目(term)全体を無効にすべきところ、その中の条件(prefix-list)だけを無効にしたため、アトランタのルーターがすべてのBGP経路をより高い優先度(local-preference 200)でバックボーン全体に広報した。各拠点が自拠点のサーバーへの経路に設定していた優先度は100だったため、バックボーンに接続された拠点のトラフィックがすべてアトランタに集中した。アトランタは過負荷になり、影響を受けた拠点では処理するトラフィックがほとんどなくなった。アトランタのルーターをバックボーンから外すと復旧した。Cloudflareは、攻撃や侵害とは関係がないと説明した。 - 教訓:特定の都市・地域のユーザーだけが一斉に切断されたり接続不可・無限ロードになったりして、ほかは正常なら、直前の経路設定の変更をまず疑う。グラフでは一つの拠点だけCPU・トラフィックが急上昇し、影響を受けた拠点はむしろ0近くまで落ちる。主担当はインフラチーム(ネットワーク)で、事業者側の障害なら外部だ。Cloudflareは、バックボーンのBGPセッションに受信できる経路数の上限(maximum-prefix)を設けることにし、ある拠点がほかの拠点のトラフィックを引き寄せられないよう優先度を調整した。 - 関連する原因:isp-bgp, rt-path - 原文:[Cloudflare](https://blog.cloudflare.com/cloudflare-outage-on-july-17-2020/) ### fastly-2021 · Fastly 2021: Fastly CDNで世界規模のエラー - 何が起きたか:多くのゲームがアップデートファイル・ランチャー・WebページをCDNで配信しているため、ゲームも巻き込まれるタイプのインフラ障害だ。2021年6月8日09:47(UTC)から、Fastlyネットワークの85%がエラーを返した。49分以内にネットワークの95%が正常に戻り、12:35に障害は収束した。 - 原因:5月12日に始まったソフトウェアのデプロイに、特定の顧客設定が特定の条件を満たしたときに発動するバグが含まれていた。6月8日、ある顧客が正当な設定変更を反映したことで、その条件がそろった。Fastlyは1分で異常を検知し、原因となった顧客設定を特定して無効にすると復旧が始まった。バグ修正のデプロイは同日17:25に開始した。 - 教訓:デプロイから数週間たったコードでも、まれな条件に当たると一瞬で世界規模の障害になる。ゲーム側で確認すべきシグナルは、アップデート・ランチャー・WebリクエストのHTTPエラー率がすべての地域で同時に上がることと、CDN事業者のステータスページだ。接続済みのゲーム通信はCDNを経由していなければ正常で、新規接続・アップデートのダウンロード・Webログインだけが止まるのが特徴だ。主担当は外部(CDN事業者)で、ゲーム開発チーム・インフラチームは、CDNを複数使うか、オリジンサーバーから直接取得する迂回経路を用意しておく。 - 関連する原因:in-external - 原文:[Fastly](https://www.fastly.com/blog/summary-of-june-8-outage) ### meta-2021 · Meta 2021: バックボーンへのコマンド一つでDNSまで消えたFacebookの障害 - 何が起きたか:ゲーム会社の自社ネットワークとDNSにもそのまま当てはまるインフラ障害だ。2021年10月4日、Facebook(現Meta)のサービスに世界中で接続できなくなった。データセンター間をつなぐバックボーンがすべて切れ、インターネット側からFacebookのDNSサーバーを見つけられなくなった。ポストモーテムには、障害の継続時間は書かれていない。 - 原因:定期メンテナンス作業中、世界全体のバックボーン容量を確認するために実行したコマンドが意図に反してバックボーンのすべての接続を切断し、こうしたコマンドを止めるはずの監査ツールはバグのため止められなかった。小規模拠点のDNSサーバーは、データセンターと通信できないと自らを異常と判断してBGP広報を取り下げる仕組みだったため、DNSサーバーは動いているのにインターネットから到達できなくなった。通常のアクセス経路も帯域外(out-of-band)アクセスもすべて断たれ、内部ツールもDNSを失ったため、エンジニアをデータセンターへ直接派遣する必要があり、セキュリティ手順のためにさらに時間がかかった。復旧時は、データセンターごとに電力使用量が数十MWずつ落ちていたため、一気に戻すと電力設備からキャッシュまで危険にさらされうると判断し、負荷を段階的に上げた。 - 教訓:すべての地域・すべての通信事業者で同時に接続不可・無限ロードが起きたら、ゲームサーバーよりも先にDNSとBGP経路を確認する。外部からのDNS問い合わせと公開されているBGP経路情報を使えば、社外からでも確認できる。主担当はインフラチーム(ネットワーク)だ。障害時に使う帯域外アクセス経路や内部ツールが同じDNS・ネットワークに依存していないかを事前に点検し、復旧時は再接続が一気に集中しないよう負荷を段階的に上げる。 - 関連する原因:isp-bgp, isp-dns - 原文:[Meta](https://engineering.fb.com/2021/10/05/networking-traffic/outage-details/) ### aws-2021 · AWS 2021: AWS us-east-1の内部ネットワーク輻輳 - 何が起きたか:多くのゲームがサーバー・ログイン・データをパブリッククラウドに置いているため、ゲームも巻き込まれるタイプのインフラ障害だ。2021年12月7日午前7時30分(太平洋標準時)、北バージニアリージョン(us-east-1)の内部ネットワークが輻輳した。7時33分からEC2 APIのエラーと遅延が増えて新しいインスタンスを起動しにくくなり(インスタンスの起動は午後2時40分に回復)、コンソールへのログイン失敗、Route 53の設定変更不可、CloudWatchメトリクスの遅延と一部欠損が続いた。ネットワーク機器は午後2時22分に完全に回復した。すでに稼働していたEC2インスタンスと既存のDNS応答は影響を受けなかった。 - 原因:メインネットワーク上のあるサービスの容量を増やす自動処理が、内部ネットワークの多数のクライアントで予期しない動作を引き起こし、接続試行が急増した。内部ネットワークとメインネットワークをつなぐ機器があふれて通信が遅延し、その遅延がさらに接続試行と再試行を増やして輻輳が続いた。クライアントにはこうした輻輳時にリクエスト間隔を広げるバックオフの仕組みがあったが、潜在的な不具合のため正しく動作しなかった。内部の監視も同じネットワークに依存していたため、運用チームはリアルタイムのメトリクスなしにログを頼りに対応した。 - 教訓:再試行の間隔を広げられないと、短い輻輳が数時間の障害になる。ゲーム側から見ると、稼働中のゲームサーバーは正常でも、新しいサーバーの増設(オートスケーリング)、クラウドAPIを使うログイン・マッチング・決済、監視がまとめて止まることがある。確認すべきシグナルは、クラウド事業者のステータスページ、クラウドAPIのエラー率、インスタンスの起動失敗だ。主担当は外部(クラウド事業者)だ。ゲーム開発チームはすべての再試行にランダムな間隔の指数バックオフと回数制限を設け、インフラチームは増設できなくても持ちこたえられる余剰容量と、別リージョンという代替手段を用意する。 - 関連する原因:in-cascade, in-autoscale, in-external - 原文:[AWS](https://aws.amazon.com/message/12721/) ### cloudflare-dns-2025 · Cloudflare 2025: CloudflareのパブリックDNS 1.1.1.1の障害 - 何が起きたか:ユーザーが端末やルーターに自分で設定して使うパブリックDNSリゾルバーの障害で、その設定を使うユーザーに限って、すべてのゲームとサービスがまとめて使えなくなるタイプだ。2025年7月14日21:52から22:54(UTC)までの62分間、1.1.1.1リゾルバーが世界中で応答しなかった。Cloudflareは、多くの利用者にとってこれは事実上すべてのインターネットサービスが使えないことを意味したと述べている。UDP・TCP・DNS over TLSによる問い合わせが影響を受け、ドメイン名で接続するDNS over HTTPSは比較的安定していた。 - 原因:6月6日、今後使う予定の別サービスのサービストポロジー(どの拠点からIPアドレス帯を広報するかを定めた構成)を準備する際、1.1.1.1リゾルバーのIPアドレス帯が誤ってその構成に含められた。7月14日にそのサービスの構成を変更すると、リゾルバーのアドレス帯を広報する拠点が全拠点からオフラインの1か所に減り、世界中でBGP経路が取り下げられた。この変更はカナリアリリースを経ずに、すべてのデータセンターへ即座に反映された。22:20に設定を戻すとトラフィックは約77%まで回復したが、その間にエッジサーバーの約23%で必要なIP設定が消えていたため再設定が必要になり、正常に戻ったのは22:54だった。Cloudflareは、攻撃やBGPハイジャックとは関係のない内部の設定ミスだと説明した。 - 教訓:ゲームサーバーもほかのユーザーも正常なのに、一部のユーザーだけがログインサーバーやアップデートサーバーに接続不可・無限ロードになるなら、そのユーザーが使っているDNSを疑う。接続済みのセッションは維持され、新規接続だけが失敗するのが特徴だ。ユーザーにDNS設定を変えてもらうか、サーバーのアドレスを直接問い合わせてもらえば、すぐに見分けがつく。主担当は外部(DNS運営者・通信事業者)だ。ゲーム開発チーム(クライアント)が名前解決の失敗をほかのエラーと区別して表示すれば、カスタマーサポートがその場で切り分けられる。 - 関連する原因:isp-dns, isp-bgp - 原文:[Cloudflare](https://blog.cloudflare.com/cloudflare-1-1-1-1-incident-on-july-14-2025/) ### aws-2025 · AWS 2025: AWS us-east-1のDynamoDB DNS障害と長引いた復旧 - 何が起きたか:多くのゲームがサーバー・ログイン・データをパブリッククラウドに置いているため、ゲームも巻き込まれるタイプのインフラ障害だ。2025年10月19日午後11時48分から20日午後2時20分(太平洋夏時間)まで、北バージニアリージョンで3段階にわたって影響が続いた。20日午前2時40分まではDynamoDB APIのエラーが増え、午前2時25分から10時36分までは新しいEC2インスタンスの起動が失敗し(一部の新しいインスタンスの接続問題は午後1時50分に解消)、午前5時30分から午後2時9分までは一部のNetwork Load Balancer(NLB)で接続エラーが増えた。 - 原因:DynamoDBのDNSを管理する自動化の仕組みに、潜在的な競合状態(race condition)があった。異なるアベイラビリティーゾーンでDNSプランを適用する実行コンポーネント(DNS Enactor)のうち、異常に遅れていた1つが古いプランで新しいプランを上書きした直後に、別の実行コンポーネントのクリーンアップ処理がその古いプランを削除し、リージョンエンドポイント(dynamodb.us-east-1.amazonaws.com)のDNSレコードが空になった。自動化の仕組みではこれを修復できず、人手で復旧する必要があった。EC2の物理サーバー管理システムはDynamoDBに依存していたため、その間に物理サーバーごとに維持していたリース(lease)が期限切れになった。DynamoDBが復旧した後は、物理サーバーの数が多すぎてリースを結び直す処理が終わる前にタイムアウトし、再試行の処理が再びたまって「輻輳崩壊(congestive collapse)」の状態に陥った。新たに起動したインスタンスのネットワーク設定の反映が遅れたため、NLBのヘルスチェックが成功と失敗を行き来し、正常なノードまでDNSから外れては戻ることを繰り返した。 - 教訓:一か所のDNSレコードの誤りが、そのサービスに依存する別のサービスへ波及し、原因が解消した後も、滞留した処理とヘルスチェックの不安定さのせいで復旧にさらに数時間かかる。ゲーム側から見ると、稼働中のサーバーは持ちこたえても新しいサーバーを起動できずにオートスケーリングが止まり、ヘルスチェックが不安定になるとロードバランサーが正常なサーバーを外してしまうことがある。確認すべきシグナルは、クラウドのステータスページ、マネージドサービスのAPIエラー率、インスタンスの起動失敗、ロードバランサーの正常なターゲット数だ。主担当は外部(クラウド事業者)で、インフラチームはヘルスチェックの失敗で一斉に外れるサーバー数を制限し、別リージョンという代替手段を用意する。 - 関連する原因:in-external, in-cascade, in-autoscale, dc-lb-imbalance, isp-dns - 原文:[AWS](https://aws.amazon.com/message/101925/) ## 用語 - **Ping** (Ping, RTT): 自分が送った信号がサーバーまで行って戻ってくるまでの時間(往復)。ゲームに表示されるPingには、サーバーの処理待ち時間が含まれていることもあります。 - **遅延** (Latency): パケットが送り出されてから到着するまでの時間。片道を指すことが多く、Pingのおよそ半分です。 - **ジッター** (Jitter): 到着間隔のばらつき。平均Pingが同じでも、ジッターが大きいと画面がカクつきます。 - **パケット** (Packet): ネットワークで一度に送るデータのまとまり。通常は最大1,500バイトで、ゲームの更新データは数十〜数百バイトです。 - **パケットロス** (Packet loss): 送ったパケットが届かずに消えること。TCPを使うゲームでは1%でも数秒〜十数秒に一度カクッと止まるのが感じられ、補間や入力の重複送信を備えたUDPのゲームなら数%まで隠せることもあります。 - **帯域幅** (Bandwidth): 回線が1秒間に送れる最大データ量(Mbps)。どれだけ早く届くか(遅延)とは別の概念です。 - **ティック** (Tick): サーバーがゲーム状態を一度計算する単位。20ティックのサーバーは1秒に20回、50msごとに計算します。 - **ティックレート** (Tick rate): 1秒間にティックを何回回すか。高いほど反応が速くなりますが、サーバーコストと通信量が増えます。通信量を抑えるため、パケットを送る回数はティックレートより低く設定することもあります。 - **ティックバジェット** (Tick budget): 1ティックを終えなければならない時間の上限。超えると次のティックが遅れ、ティック間隔が延びます。 - **FPS** (Frames per second): 1秒間に画面を何回描画するか。60FPSなら1フレームあたり16.7ms。 - **フレームタイム** (Frame time): 1フレームの描画にかかった時間。平均FPSよりも、ときどき跳ねるフレームのほうが体感には重要です。 - **スナップショット** (Snapshot): サーバーがティックごとに送る「今のゲーム状態」の要約。位置、HP、状態などが入っています。たいていは、受信側がすでに持っている状態との差分だけを送ります(デルタ圧縮)。 - **補間** (Interpolation): 受信した2つのスナップショットの間をつないで描き、滑らかに見せる技術。その代わり、少し過去の状態を表示します。 - **補間バッファ** (Interpolation buffer): 補間のためにわざと遅らせて描く時間。ジッターや1〜2個のパケットロスを吸収する余裕です。通常はパケット間隔の2倍(1秒に20回受信するなら100ms)で、ジッターが大きくなると自動で延ばすゲームもあります。 - **外挿** (Extrapolation, Dead reckoning): 新しいパケットが来ないとき、最後の速度からこの先どこにいるかを推測して描く技術。外れるとワープのように見えるため、多くのゲームは0.25秒前後までしか推測せずに止めます(Sourceエンジンのデフォルト値は0.25秒)。 - **クライアントサイド予測** (Client-side prediction): サーバーの確認を待たずに、自分のキャラクターを先に動かして見せる技術。 - **サーバー補正** (Reconciliation): サーバーの結果が届いたら予測と比べ、自分のキャラクターの位置を修正すること。サーバーが確認した位置から、まだ確認されていない自分の入力を適用し直して計算します。差が大きいと引き戻しのように見えます。 - **ラグコンペンセーション** (Lag compensation): サーバーが攻撃の当たり判定をするとき、攻撃者が見ていた過去の時点まで巻き戻して当たったかどうかを確かめる技術。撃たれた側が理不尽に感じないよう、巻き戻す幅に上限を設けます。競技性の高いシューターでは0.2〜0.25秒前後が一般的で、Sourceエンジンのデフォルト値のように1秒まで巻き戻す例もあります。 - **サーバー権威型** (Authoritative server): 最終的な判定はサーバーだけが行うという設計。チートを防げますが、すべての結果がサーバーとの往復を経ます。そのため予測・先行演出で待ち時間を隠します。 - **ロックステップ** (Deterministic lockstep): 全員が入力だけをやり取りし、同じターンで同じ計算をする方式。入力に一定の遅延を付け、誰か一人の入力が遅れると全員が待ちます。 - **サーバー側入力バッファ** (Server-side input buffer): サーバーがプレイヤーごとに入力を少しためておき、1ティックに1つずつ取り出して使うバッファ。ジッターが大きい人もほかの人からは滑らかに見えますが、その人の行動がサーバーで確定するタイミングはその分遅れます。 - **リッスンサーバー** (Listen server): プレイヤー1人のPCが、ゲームをしながらサーバーの役割も兼ねる方式。ホストはPingが0ですが、ホストの回線やPCが遅いと全員がラグの影響を受けます。 - **フェーズ** (Phasing): 同じ場所でも、クエストの進行度によって見えるNPCや地形を変える機能。2人のキャラクターの進行度が違えば、片方にだけNPCがいないのは正常です。 - **ロールバックネットコード** (Rollback netcode (GGPO)): 相手の入力を予測して先に進め、実際の入力が違っていたら過去のフレームまで巻き戻して計算し直す方式。格闘ゲームでよく使われます。DBのロールバックとは別の意味です。 - **先行入力** (Input buffer, spell queue): クールタイムや動作が終わる少し前に押された次の入力を受け付けておき、終わった瞬間に実行すること。連続技の合間に往復時間が挟まらないようにします。 - **先行演出** (Client-side feedback): サーバーの確認を待たずに、アニメーション・サウンド・エフェクトを先に再生すること。ダメージ・報酬のように確定が必要な結果だけ、サーバーの応答を待ちます。サーバーが拒否したら、見せたものを元に戻す必要があります。 - **TCP** (Transmission Control Protocol): 順番どおりに、漏れなく届けるプロトコル。失われたパケットを受け取り直すまで、後続のパケットをゲームに渡しません。 - **UDP** (User Datagram Protocol): 何の保証もなく、送ったとおりに届けるプロトコル。待ちがない代わりに、パケットロスや順序はゲームが自分で処理します。 - **信頼性UDP** (Reliable UDP (KCP, ENet…)): UDPの上に、必要な分だけ再送・順序保証を自前で実装した方式。 - **HOLブロッキング** (Head-of-line blocking): 先頭の1つが詰まると、後ろのすべてが待たされる現象。TCPで早送りが起きる原因です。 - **RTO** (Retransmission timeout): 再送タイマー。TCPがパケットを失ったと判断して送り直すまでに待つ時間。LinuxではPing + 200ms以上で、失敗するたびに2倍になります。 - **Nagleアルゴリズム** (Nagle’s algorithm): 先に送ったデータの確認(ACK)が返るまで小さなデータをためておき、まとめて送ることでパケット数を減らすTCPの機能。ゲームではたいていオフにすべきです。 - **TCP_NODELAY** (TCP_NODELAY): Nagleアルゴリズムを無効にするソケットオプション。小さなメッセージをすぐに送ります。 - **遅延ACK** (Delayed ACK): 受け取ったという確認を少し遅らせて、ほかのデータとまとめて送る機能。Linuxは通常40ms(最大200ms)、Windowsは古いバージョンが200msで、最近のバージョンは40ms。 - **ソケットバッファ** (SO_SNDBUF / SO_RCVBUF): OSがソケットごとに持つ、送信・受信の待機領域のサイズ。小さすぎるとあふれ、大きすぎると古いデータがたまって待たされます。 - **keepalive** (SO_KEEPALIVE): アイドル接続が生きているかを確認するTCPの機能。デフォルトでは無効で、有効にしてもデフォルト設定では2時間後にようやく確認します。 - **RST** (TCP reset): 接続をその場で強制的に切るTCPの信号。まだ送れていないデータは破棄されます。 - **ハートビート** (Heartbeat): ゲームが自ら定期的に送る「生きている」という信号。切れた接続の検知や、途中の機器で接続を維持するのに使います。 - **タイムアウト** (Timeout): この時間応答がなければ失敗とみなす基準。短すぎると誤判定、長すぎると検知が遅れます。 - **NAT** (Network Address Translation): ルーターが家庭内の複数の機器を1つのグローバルIPで外に出し、その接続をNATテーブルに記録する機能。 - **CGNAT** (Carrier-grade NAT): 通信事業者が、複数の契約者に1つのIPアドレスを共有させる大規模なNAT。 - **MTU** (Maximum Transmission Unit): 一度に送れるパケットの最大サイズ。通常は1,500バイトで、VPN・PPPoEの区間ではさらに小さくなります。 - **バッファブロート** (Bufferbloat): 機器がキューを大きくため込みすぎて、遅延が数百msまで膨らむ現象。 - **SQM** (Smart Queue Management (fq_codel, CAKE)): キューを短く保ち、フローごとに公平に送り出すルーターの機能。バッファブロートの解決策です。 - **QoS** (Quality of Service): 重要なトラフィックを優先して送るよう、優先度を付ける機能。 - **ピアリング** (Peering): 通信事業者どうしが互いのネットワークを接続する地点。夜に混雑しやすくなります。 - **BGP** (Border Gateway Protocol): インターネットでどの経路を使って送るかを、通信事業者どうしで伝え合うためのプロトコル。変わると経路とPingが変わります。 - **DDoS** (Distributed Denial of Service): 多数の場所から大量のトラフィックを送りつけ、サービスを停止させる攻撃。 - **スクラビングセンター** (DDoS scrubbing center): DDoS攻撃の際、サーバー宛てのトラフィックをいったん引き受けて攻撃を取り除き、正常なトラフィックだけを渡すDDoS対策事業者の拠点。拠点が遠いと経路が長くなります。 - **ファイアウォール** (Firewall): 許可された接続だけを通す機器やソフトウェア。接続をセッションテーブルで追跡します。 - **ロードバランサー** (Load balancer): 入ってくる接続を、複数のサーバーに振り分ける機器。 - **セッションテーブル** (Session table, conntrack): 機器やOSが、現在の接続を追跡するテーブル。サイズに上限があります。 - **マイクロバースト** (Microburst): 平均は低くても、1ms以下のごく短い瞬間にトラフィックが集中する現象。 - **NIC** (Network Interface Card): サーバーのネットワークカード。 - **リングバッファ** (Ring buffer): NICが受信したパケットを、CPUが取り出すまで入れておくバッファ。決まった数のスロットを使い回し、スロットがすべて埋まると新しいパケットは破棄されます。 - **割り込み** (Interrupt): デバイスがCPUに「処理すべきことが発生した」と知らせる信号。 - **RSS** (Receive Side Scaling): 受信したパケットを複数の受信キューに分け、複数のCPUコアで処理させるNICの機能。 - **PPS** (Packets per second): 1秒あたりのパケット数。ゲームサーバーは、帯域幅よりもこの数値で先に限界に達しがちです。 - **カーネル** (Kernel): OSの中核。ネットワーク、メモリ、CPUの割り当てを担います。 - **backlog** (Listen backlog): サーバーがまだ受け取っていない新規接続の要求が待つキュー。キューが満杯になると、Linuxは新しい要求を黙って破棄し、Windowsは拒否の応答を返します。 - **TIME_WAIT** (TIME_WAIT): 先に接続を閉じた側が、遅れて届くパケットに備えて、そのポートの組み合わせをしばらく(Linuxでは60秒)保持する状態。 - **CPUスチール** (Steal time): 仮想マシンがCPUを使おうとしたのに、物理サーバーがほかの仮想マシンにCPUを割り当てていたために待たされた時間。topのst値で確認します。 - **CPUスロットリング** (CFS throttling): コンテナが決められた周期(CFS period、通常100ms)の中でCPUのクォータ(quota)を使い切ると、次の周期まで強制的に止められること。 - **ファイルディスクリプタ** (File descriptor): プロセスが開いたファイル・接続ごとに付く番号(fd)。数に上限があります。 - **スレッド** (Thread): プログラムの中で独立して実行される処理の単位。複数のスレッドが同時に実行されることもあります。 - **コンテキストスイッチ** (Context switch): CPUが実行中のスレッドを別のスレッドに切り替えること。コストがかかります。 - **ロック** (Lock, Mutex): 共有データを一度に1つのスレッドだけが使えるようにする排他制御。 - **デッドロック** (Deadlock): スレッドどうしが互いに相手の持つロックを待ち、永遠に止まってしまった状態。 - **スレッドプール** (Thread pool): あらかじめ作っておいたワーカースレッドの集まり。すべて使用中だと、新しい処理は待たされます。 - **非同期I/O** (epoll, IOCP, io_uring): 入出力の完了を待たずにほかの処理を進め、完了したら通知を受け取る方式。 - **AOI** (Area of Interest): 各プレイヤーが「見える範囲」。この範囲内の変化だけを送って通信量を減らします。誰が範囲内にいるかを調べるコストを下げるため、通常はマップをグリッドに分けて近くのセルだけを調べます。 - **ブロードキャスト** (Broadcast, fan-out): 1つの変化を、それが見える複数のプレイヤーに送ること。集まった全員が互いを見ている場合、送る量は人数の2乗で増えます。 - **GC** (Garbage collection): 使い終わったメモリを自動で回収する機能。GCの間、プログラムが止まることもあります。 - **ヒープ** (Heap): プログラムが実行中に、必要に応じて確保して使うメモリ領域。 - **メモリリーク** (Memory leak): 使い終わったメモリを解放しないため、使用量が増え続けるバグ。GCがあっても、使い終わったオブジェクトをどこかで参照し続けていると起きます。 - **スワップ** (Swap, paging): RAMが足りず、メモリの一部をディスクに退避すること。退避したメモリを再び使うときは、RAMより1,000倍以上遅くなります。 - **OOM Killer** (Out-of-memory killer): メモリが枯渇すると、Linuxがメモリを最も多く使っているプロセスを選んで強制終了する機能。コンテナでは、メモリ上限に達しただけでも発動します。 - **キャッシュミス** (Cache miss): CPUに近いキャッシュにデータがなく、遅いメモリまで取りに行かなければならないこと。 - **IOPS** (I/O operations per second): ディスクが1秒間に処理できる読み書きの回数。クラウドのディスクは、支払った料金に応じて上限が決まります。 - **fsync** (fsync): データがディスクに確実に書き込まれるまで待つ命令。通常の書き込みはまずOSのメモリに入り、あとからディスクに書き出されるため、その間にサーバーの電源が落ちると失われることがあります。fsyncは安全ですが遅くなります。 - **バーストクレジット** (Burst credits): クラウドのディスク・サーバーが一時的にベースライン以上の性能を出せるように、ためておくクレジット残高。使い切るとベースライン性能に落ちます。 - **インデックス** (Index): DBの索引。ないとテーブル全体を読む必要があります。 - **フルスキャン** (Full table scan): インデックスを使わずに、テーブルの全行を調べる検索。 - **実行計画** (Query plan): DBがクエリをどの順序で、どのインデックスを使って処理するかを決めた方法。コードが同じでも、DBが実行計画を変えると同じクエリが急に遅くなることがあります。 - **トランザクション** (Transaction): 「すべて成功するか、すべて失敗するか」を1つにまとめたDBの処理。取引は必ずトランザクションで処理します。終わるまで更新した行をロックしておくため、短いほど良いです。 - **コネクションプール** (Connection pool): あらかじめ確立しておいたDB接続の集まり。すべて使用中だと、新しいリクエストは待たされます。 - **ホットスポット** (Hot row): 多くのリクエストが同時に更新しようとする1つの行。ロック競合の原因。 - **レプリケーション遅延** (Replication lag): レプリカのDBがプライマリのDBに追いつけず、遅れている時間。 - **ロールバック** (Rollback): 保存が取り消されて、以前の状態に戻ること。プレイヤーは「アイテムが消えた」と感じます。 - **キャッシュ** (Cache (Redis etc.)): よく使うデータを、高速な場所にコピーしておくもの。DBの負荷を減らします。 - **チェックポイント** (Checkpoint): DBがメモリにためた変更分を、定期的にまとめてディスクに書き込む処理。その瞬間、保存・参照が一時的に遅くなることがあります。 - **フェイルオーバー** (Failover): プライマリのサーバーやDBが落ちたとき、スタンバイ側に切り替えること。切り替え中はしばらく保存できず、レプリケーションが遅れていれば直近のデータが失われることがあります。 - **MVCC** (Multi-version concurrency control): 読む側と更新する側が互いをブロックしないよう、DBが古いバージョンを一時的に保持する方式。長時間開いたままのトランザクションがあると、古いバージョンがたまって遅くなります。 - **キャッシュスタンピード** (Cache stampede): キャッシュが一斉に切れて、リクエストがオリジン(DB)に殺到する現象。 - **ゲートウェイ** (Gateway): クライアントの接続を受けて、後ろのゲームサーバーに中継する中間サーバー。 - **サーキットブレーカー** (Circuit breaker): 失敗が続くサービス呼び出しを一時的に遮断し、すぐに失敗として扱うことでカスケード障害を防ぐ仕組み。しばらくしてから1〜2回試しに呼び出し、復旧していれば呼び出しを再び許可します。 - **カスケード障害** (Cascading failure): 1か所の障害が、呼び出しチェーンをたどってほかのサービスに広がること。 - **オートスケーリング** (Autoscaling): 負荷に応じてサーバー台数を自動で増減する機能。増えるまでに時間がかかります。 - **ウォッチドッグ** (Watchdog): サーバーが止まっていないかを監視するタイマー。ゲームループが決められた時間(数秒〜数十秒)以上止まると、状態の記録(ダンプ)を残してサーバーを強制終了し、再起動させます。 - **利用率** (Utilization): ワーカー(CPUコア・スレッド・DBコネクションのように、リクエストを処理する主体)がビジーな時間の割合。80〜90%を超えると待ちが急激に増えます。 - **p99** (99th percentile): 100回のうち99回はこれより速く、1回はこれより遅い値。平均よりも体感のラグをよく表します。 - **V-Sync** (Vertical sync): 画面のリフレッシュ周期に合わせてフレームを出力する機能。ティアリング(画面のずれ)はなくなりますが入力遅延が生じ、FPSがリフレッシュレートを下回ると60と30の間を行き来してカクつくこともあります。 - **可変リフレッシュレート** (VRR, G-Sync, FreeSync): フレームの準備ができたタイミングに合わせて、モニターが画面を更新する機能。V-Syncで60と30を行き来して起きるカクつきと入力遅延を減らします。 - **アンチチート** (Anti-cheat): ゲームのチート(不正改造)を防ぐセキュリティモジュール。定期的な検査やサーバーとのハートビートが失敗すると、カクつきや切断を引き起こすこともあります。 - **オーバーレイ** (Overlay): チャット・録画・FPS表示などのソフトが、ゲーム画面の上に描き重ねる機能。ゲームの描画処理に割り込むため、カクつきの原因になることがあります。 - **シェーダーコンパイル** (Shader compilation): グラフィック効果のプログラムをGPU用に変換する処理。事前に済ませておかないと初めて表示するときに画面が一瞬止まり、グラフィックドライバーを更新すると保存済みの結果が無効になってやり直します。 - **メインスレッド** (Main thread, Game thread): ゲームロジックの計算と描画の準備を順番に処理する、ゲームの中心となるスレッド。ここで時間のかかる処理が1つでもあると、その間画面が止まります。 - **タイマー分解能** (Timer resolution): OSが、スリープ中のプログラムを起こせる最短の間隔。Windowsのデフォルト値は15.6msなので、プログラムが個別に変更しなければ「1ms後に起こして」と頼んでも遅れて起きます。 - **サーマルスロットリング** (Thermal throttling): 端末が熱くなると、自らCPU・GPUのクロックを下げる保護機能。スマホでは数分〜数十分ゲームをすると頻繁に起きます。 - **VRAM** (Video memory): グラフィックカードに搭載された専用メモリ。テクスチャやモデルをここに載せて描画します。足りなくなると、PCのメモリと遅い経路でやり取りするためカクつきます。 - **ネットグラフ** (Net graph): ゲーム画面にPing・パケットロス・FPS・ティックをリアルタイムのグラフで表示する、開発・デバッグ用の表示。ラグ報告の動画に一緒に映っていると、原因の特定がずっと楽になります。 - **再送率** (Retransmission rate): 送信したTCPパケットのうち、再送した割合。公的な基準はありませんが、サーバー全体の平均が0.1%未満なら健全な部類で、1%を超えると多くのユーザーがラグを感じやすくなります。普段の値の何倍に増えたかもあわせて見ます。 - **SACK** (Selective ACK): 受信側が「この範囲は受け取り、この部分だけ抜けている」と詳しく伝えるTCPの機能。複数のパケットを失っても、一度に回復できます。 - **RACK-TLP** (Recent ACK, Tail Loss Probe): 時間を基準にロスを判断し、しばらくACKが来なければ末尾のパケットをもう一度送って回復を早めるTCPの機能。最新のLinux・Androidのデフォルトです。WindowsはWindows 10(1607)・Server 2016からTLPとRACKがデフォルトで、失った再送パケットまで回復する新しいRACKはServer 2022からです。SACKが有効な接続でのみ動作します。 - **不要な再送** (Spurious retransmission): 失われていないのに、遅れて届いたり順序が入れ替わったりしたためにロスと誤認して送り直したもの。回線を無駄にし、送信量を不必要に絞ってしまいます。 - **ゼロウィンドウ** (Zero window): 受信側のバッファが満杯になり、「しばらく送らないで」と通知した状態。再送のように見えますが回線は正常で、受信側のプログラムが時間内に読み出せていないことが原因です。 - **thin stream** (Thin stream): ゲームのように、小さなパケットをまばらに送る接続。高速再送のきっかけとなる信号が集まりにくく、ロスのときに長く止まります。 - **ポリサー** (Policer): 決められた速度を超えたパケットを、キューに入れずにすぐ破棄する帯域制限の方式。キューに入れてからゆっくり送り出す方式はシェーパーと呼びます。 - **ペーシング** (Pacing): 送るパケットを一度に吐き出さず、時間的に均等に分けて送ること。小さなバッファがあふれるのを防ぎます。 - **ECN** (Explicit Congestion Notification): 輻輳時にパケットを破棄せず「輻輳あり」の印を付け、送信側に速度を落とさせる機能。ロスなしで輻輳を伝えます。両端と、混雑している区間の機器がすべて対応していないと効果がありません。 - **MSS** (Maximum Segment Size): TCPが1つのパケットに入れるデータの最大サイズ。通常は1,460バイトで、トンネル区間に合わせて小さくするとMTUブラックホールを防げます。 - **ハンドオーバー** (Handover): 移動中のスマホが、接続先の基地局を切り替えること。 - **パーセンタイル** (Percentile (p50, p95, p99)): 値を小さい順に並べたとき、何%目に来る値か。p50は中央値、p99は100回のうち最も遅い1回付近の値です。平均では隠れてしまうスパイクがわかります。 - **テールレイテンシ** (Tail latency): 大半は速いのに、ときどき発生する長い遅延。平均にはほとんど表れませんが、ユーザーがラグとして記憶するのはこの部分です。 - **外形監視** (Synthetic monitoring): 実際のユーザーの代わりに、計測用の端末・サーバーが決まった場所から定期的にping・tracerouteなどを送り、経路の品質を測ること。RIPE Atlasが代表的な公開ツールです。 - **集計間隔** (Aggregation interval): グラフの1点が、何秒・何分の値をまとめたものか。間隔が長いほど、短いスパイクが平均に埋もれて見えにくくなります。 - **ポストモーテム** (Postmortem): 障害が収まった後に、何が起き、なぜ起き、何を変えるかをまとめた文書。責任追及より再発防止を目的に書きます。 - **C-state** (CPU idle state): CPUが休んでいるときに入る省電力状態。深い状態ほど電力を節約できますが、復帰に時間がかかります。 - **ライブマイグレーション** (Live migration): クラウドがホストのメンテナンスなどのために、実行中の仮想マシンを別のホストに移すこと。移す瞬間に一時的に止まることがあります。 - **SNAT** (Source NAT): 送信パケットの送信元アドレスを、グローバルアドレスに書き換えるNAT。1つのグローバルアドレスで使えるポート数には上限があり、使い切ると新しい接続が失敗します。 - **NATゲートウェイ** (NAT gateway): プライベートネットワーク内のサーバーがインターネットに出るとき、1つのグローバルアドレスを共有させるクラウドの仕組み。宛先ごとの同時接続数に上限があります。 - **低軌道衛星インターネット** (LEO satellite internet): 高度数百〜数千kmの衛星群で接続するインターネット。静止軌道衛星より遅延はずっと短いものの、接続する衛星が切り替わるときに遅延が跳ねることがあります。 - **GeoIP** (IP geolocation): IPアドレスから国・都市・通信事業者を推定するデータベース。誤った項目や古い項目があり、遠い地域のサーバーに割り当てられる原因になることもあります。 - **TLS証明書** (TLS certificate): サーバーが本物であることを証明する電子文書。有効期限があり、期限が切れると暗号化通信の確立に失敗して接続できなくなります。 - **フレーム生成** (Frame generation): グラフィックカードが実際に描いたフレームの間に予測したフレームを挟み込み、FPSを上げる技術。画面は滑らかになりますが、入力から画面表示までの遅延は増えることがあります。