Основная версия: https://jungrok5.github.io/mmo-lag-anatomy/ru/ Страницы причин: https://jungrok5.github.io/mmo-lag-anatomy/ru/c/[ID причины].html # Анатомия игровых лагов: данные справочника Сгенерировано автоматически (2026-10-03, `node tools/export.cjs`). Вручную не править: изменения вносятся в src/js/, после чего файл выгружается заново. Причин: 228, терминов: 135. Причины обозначаются **ID**. Если добавить к адресу сайта `#c-ID`, откроется карточка этой причины. ## Коды ответственных | Код | Команда | Ответственные | Охват | |---|---|---|---| | cli | Команда разработки | Разработка клиента | Код игрового клиента: кадры, GC, загрузка; интерполяция, экстраполяция, предсказание; сетевая часть клиента (включая отправку хартбитов и автоматическое переподключение) | | srv | Команда разработки | Разработка сервера | Код игрового сервера: тики, потоки, блокировки; архитектура синхронизации; приём подключений (цикл accept, аргумент listen); ответы на хартбиты и очистка оборванных соединений; опции сокетов; проектирование запросов и транзакций | | net | Команда инфраструктуры | Сетевая инфраструктура | Линии связи и сетевое оборудование ЦОД (коммутаторы, маршрутизаторы, файрволы, балансировщики нагрузки, защита от DDoS), сетевые ACL, маршрутизация VPC и балансировщики нагрузки в облаке, провайдеры и пиринг | | sys | Команда инфраструктуры | Серверная инфраструктура | Серверы и облачные инстансы (включая группы безопасности и отслеживание соединений), настройки ОС и ядра, NIC, среда деплоя и мониторинга | | dba | Команда инфраструктуры | Инфраструктура БД | Серверы и хранилища БД; настройки, репликация и резервное копирование БД; кэш-серверы | | ext | Внешние стороны | Внешние стороны | ПК и домашняя сеть игрока, участки сети провайдеров (вне наших договоров), облачные провайдеры. Исправить напрямую нельзя, поэтому действуем через подсказки игрокам, запросы внешним сторонам и обходные пути | «Основной ответственный» устраняет первопричину, «Совместно» отмечает стороны, у которых тоже есть реальные задачи. ## Симптомы - **Микрофризы** (`stutter`, иначе говорят: статтеры, картинка дёргается, будто проседает FPS): Движение теряет плавность: короткие остановки чередуются с рывками. Если пинг в норме, скорее всего, проблема в кадрах на ПК игрока (клиент или ОС). Если пинг скачет, вероятнее джиттер в Wi-Fi или на линии связи. Учтите, что внутриигровой пинг обычно измеряется в игровом цикле, который выполняется раз в кадр, поэтому при скачках кадров может скакать и значение пинга. - **Телепортация** (`teleport`, иначе говорят: телепорты, варп, персонажа резко перебрасывает): Персонаж без промежуточного движения сразу оказывается далеко от прежнего места. Обычно это значит, что пакеты какое-то время не приходили. Проверьте потери, короткие обрывы связи, остановки сервера, ошибки экстраполяции. Если у всех всё в порядке, а скачет один игрок, первым делом подозревают его подключение. - **Откидывание назад** (`rubber`, иначе говорят: тянет назад, rubber banding, роллбэк позиции): Персонаж бежит вперёд, и его утягивает обратно на только что пройденное место. Картинка на экране игрока (предсказание) разошлась с решением сервера. Ввод не дошёл до сервера (потери), серверная проверка перемещения его отклонила, или клиент и сервер по-разному считают движение. - **Перемотка** (`burst`, иначе говорят: ускоренная перемотка, всё разом, урон прилетает пачкой): Замерший экран снова оживает, и накопившиеся движения, удары и урон проносятся разом на большой скорости. Где-то по пути пакеты копились, а потом разом освободились. Типичные случаи: TCP ждёт повторной передачи, сервер нагоняет отставание, клиент не успевает обрабатывать пакеты. - **Слоумо** (`slowmo`, иначе говорят: мир замедлился, всё тормозит): Всё движется медленно. Применение умений и перемещение монстров выглядят растянутыми. В зависимости от устройства сервера скорость может остаться прежней, и тогда проблема проявляется как микрофризы и телепортация. Сервер не успевает вовремя завершать тики. Линия связи в порядке, поэтому пинг, измеренный вне игры, не меняется. Внутриигровой пинг может немного вырасти, если в него входит ожидание обработки на сервере. Проверьте резкий рост онлайна, расчёт зоны видимости, рассылку, нехватку памяти. - **Задержка ввода** (`delay`, иначе говорят: инпут-лаг, запоздалый отклик, ватное управление): Между нажатием и результатом проходит заметное время. При этом картинка может оставаться плавной. Пинг (время пути туда и обратно) слишком велик, или где-то растёт очередь. Проверьте расстояние, очередь роутера, Nagle (функция TCP, которая копит мелкие пакеты и отправляет их вместе), серверные очереди. Если пинг низкий, а управление всё равно ватное, проверьте ПК игрока (V-Sync, низкий FPS) или архитектуру, где каждое действие ждёт подтверждения сервера (глава о моделях синхронизации). - **Фриз** (`freeze`, иначе говорят: всё замерло, стоп-кадр, «не отвечает»): Всё на экране ненадолго (от 0,5 с до нескольких секунд) замирает, а потом снова движется. Целиком встал сервер (GC, дедлок, синхронный вызов), ненадолго оборвалась связь или остановился ПК игрока. - **Съеденные действия / роллбэк** (`dropped`, иначе говорят: умение не прожалось, предмет вернулся назад, обмен сорвался): Действие, которое точно было сделано, как будто не происходило, или его результат отменяется намного позже. Запрос потерялся (потери, переполнение очереди), сервер решил иначе, чем показал экран игрока (разный момент проверки, отказ после опережающего фидбека), или сбой случился во время сохранения (блокировки или отказ БД, падение сервера). - **Дисконнект** (`disconnect`, иначе говорят: выкидывает из игры, обрыв соединения, «Соединение с сервером потеряно»): Посреди игры соединение рвётся, и игрока возвращает на экран входа или в окно переподключения. За время таймаута не пришло ни одного пакета. Проверьте долгие обрывы связи, таймаут простоя, падение или перезапуск сервера, сервер или ПК игрока, который стоял дольше таймаута (долгая загрузка). Если игра просто закрылась без сообщения, сначала проверьте аварийное завершение клиента (краш, нехватка памяти), а соединение потом. - **Ошибка входа / бесконечная загрузка** (`noconnect`, иначе говорят: не заходит в игру, загрузка не заканчивается): В игру не пускает, или всё застревает на экране загрузки или входа. Переполнено звено, которое принимает новые подключения (очередь подключений сервера, файрвол, сервер авторизации, БД). Особенно часто сразу после техработ. - **Невидимки / фантомы** (`invisible`, иначе говорят: не видно NPC, невидимый персонаж, убитый монстр продолжает стоять): NPC, монстра или игрока, которые должны быть рядом, нет только на вашем экране, или уже исчезнувший объект остался только у вас. Со скоростью это обычно не связано: выпал один пакет или не удалась отрисовка. Проверьте разницу каналов или фаз, потерянные уведомления о появлении и исчезновении объектов, пакеты, отброшенные во время загрузки, ошибки загрузки ассетов. Решающая подсказка: появляется ли объект, если уйти из зоны видимости и вернуться. ## Четыре фактора - **Задержка** (`lat`, Latency): Из-за расстояния, очередей и времени обработки все пакеты приходят с одинаковым опозданием. Как справляется игра: Свои действия игрок видит сразу благодаря предсказанию и опережающему фидбеку, а попадания сервер проверяет, отматывая время назад (компенсация задержки). - **Джиттер** (`jit`, Jitter): В среднем всё в порядке, но одни пакеты приходят быстро, а другие с опозданием. Причины: Wi-Fi, перегруженная линия связи, занятый CPU. Как справляется игра: Игра копит немного пакетов в буфере интерполяции и достаёт их для отрисовки с постоянной скоростью. Если джиттер больше буфера, скрыть его не получится. - **Потери** (`loss`, Packet loss): Пакеты выбрасывают переполненные очереди, радиопомехи и неисправное оборудование. Короткий обрыв связи тоже означает потерю нескольких пакетов подряд. Как справляется игра: Игры на UDP закрывают пропуски интерполяцией и экстраполяцией, а ввод игрока отправляют с дублированием, чтобы потеря одного-двух пакетов ничего не меняла. TCP не отдаёт игре следующие пакеты, пока заново не получит потерянный. - **Остановка** (`stall`, Stall): Тик сервера запаздывает или встаёт (GC, блокировки, синхронные вызовы, перегрузка), кадры на ПК игрока замирают. Случается и при исправной линии связи. Как справляется игра: Отставание, накопленное за время остановки, нагоняют рывком, пропускают или дают времени течь медленнее. ## Причины ### L1 Процесс игрового клиента (причин: 16) #### cg-hitch · Всплески времени кадра · Frame hitch Расчёт одного кадра занимает в несколько раз больше времени, чем обычно, и картинка на миг замирает. - Почему → Следствие → На экране: Наплыв эффектов умений, массовый спавн и полное обновление UI приходятся на один кадр → Кадр не укладывается в 16,7 ms и занимает 50–300 ms → Картинка на миг замирает, а в следующем кадре все разом сдвигаются - Симптомы: Микрофризы, Фриз / Факторы: Остановка - У кого: Только у меня / Когда: При наплыве игроков, При определённом действии, Изредка, случайно - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Разбивать тяжёлую работу на несколько кадров, искать долгие кадры профайлером, ограничить число эффектов. - Цифры для ориентира: При 60 FPS один кадр длится 16,7 ms. Хватает одного кадра длиннее 50 ms, чтобы игроку показалось, что игра «дёрнулась». - На графике: Случайные всплески (Время кадра) - Где смотреть: Записать в PresentMon время кадра (FrameTime) и время, которое CPU и GPU потратили на этот кадр (CPUBusy, GPUBusy). На мобильных смотреть метрики медленных сессий и медленного рендеринга в Android vitals - Подтверждает: Время кадра, обычно около 16,7 ms, подскакивает выше 50 ms в моменты эффектов умений, массового спавна и полного обновления UI, а пинг в эти моменты не меняется - Опровергает: Если время кадра ровное, а дёргаются только другие персонажи, проблема на стороне сети, например «Буфер интерполяции отсутствует или слишком короткий». Если всплески идут через равные промежутки, сначала проверить причину «Сборка мусора на клиенте» - Чем проверить: Проверка на стороне игрока - Источники: - [Slow rendering](https://developer.android.com/topic/performance/vitals/render) · Android (Google) · Для 60 FPS кадр нужно отрисовать за 16 ms. Если не успеть, кадр пропускается и картинка дёргается (jank) - [Slow Sessions (games only)](https://developer.android.com/topic/performance/issues/slow-session) · Android (Google) · Android vitals считает кадр игры медленным, если он длиннее 50 ms (20 FPS) или 34 ms (30 FPS) - [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 заранее, когда пауза не мешает, например на экране загрузки. - Цифры для ориентира: Обычно от единиц ms до 100 ms за раз, на слабых телефонах и в играх, которые расходуют много памяти, дольше (в эксперименте около 150–170 ms). GC в Unity при каждом запуске проверяет всю кучу, поэтому чем больше памяти занимает игра, тем дольше пауза. - На графике: Всплески с постоянным периодом (время кадра, моменты запуска GC) - Где смотреть: В development-сборке смотреть маркеры GC.Collect и GC.Alloc в профайлере Unity. В Unreal смотреть stat GC и stat Hitches (пишет в лог кадры дольше порога t.HitchFrameTimeThreshold) - Подтверждает: В каждом долгом кадре есть участок GC.Collect примерно той же длины, что и всплеск, а всплески повторяются через равные промежутки от нескольких секунд до десятков секунд. В людных местах растёт GC.Alloc на кадр - Опровергает: Если в долгих кадрах нет участка GC, это «Синхронная загрузка и компиляция шейдеров в главном потоке» или «Нагрузка на рендеринг при большом скоплении игроков» - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Типично для клиентов на C#, например на Unity. Главный виновник: код, который каждый кадр заново создаёт строки боевого лога, цифры урона и тексты UI. Если микрофризы бывают только в людных местах, значит, где-то есть код, который создаёт тем больше мусора, чем больше вокруг игроков. Инкрементальный GC собирает мусор понемногу в каждом кадре (в Unity по умолчанию 3 ms), но если мусор появляется быстрее, чем собирается, в итоге всё равно случается одна большая пауза. В Unreal Engine тоже есть собственный GC, который убирает неиспользуемые игровые объекты. С настройками по умолчанию он запускается примерно раз в минуту (зависит от версии движка и настроек) и может давать рывок раз в минуту. В клиентах, где игровая логика написана на скриптовом языке вроде 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 (квант времени) по умолчанию 3 ms (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 · Настройка, по которой GC в Unreal запускается с заданным интервалом (Time Between Purging Pending Kill Objects, в секундах). Значения по умолчанию в документе нет - [Profiler markers reference](https://docs.unity3d.com/Manual/profiler-markers.html) · Unity · GC.Collect: время, пока код программы стоит из-за сборки мусора (от менее 1 ms до сотен 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 с только в первый раз, со второго раза всё нормально - Симптомы: Фриз, Микрофризы / Факторы: Остановка - У кого: Только у меня / Когда: В движении и при смене локации, При определённом действии - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Загружать асинхронно, подгружать заранее (прогрев), компилировать шейдеры заранее на экране загрузки или при первом запуске, использовать экраны загрузки. - Цифры для ориентира: Компиляция одного шейдера занимает десятки ms, в тяжёлых случаях больше 100 ms. Загрузка текстуры, в зависимости от скорости накопителя, от десятков до сотен ms. - На графике: Всплеск сразу после входа или техработ (число всплесков времени кадра (сразу после патча или обновления драйвера)) - Где смотреть: Дважды записать в PresentMon один и тот же маршрут и сравнить первое прохождение со вторым. В development-сборке Unreal включить r.PSOPrecache.Validation и смотреть stat PSOPrecache и строки «PSO PRECACHING MISS» в логе, в Unity смотреть в режиме Timeline профайлера участки загрузки и шейдеров в долгих кадрах - Подтверждает: Всплески на 0,1–1 с только в новых местах и при первом применении умения, во второй раз их нет. Сразу после патча или обновления графического драйвера жалоб много, потом становится меньше - Опровергает: Если всплеск каждый раз в одном и том же месте, кэш шейдеров ни при чём. Если он повторяется при каждом перемещении и только на ПК с медленным накопителем, это «Стриминг ассетов отстаёт из-за медленного накопителя», а если выделенная память GPU заполнена, это «Нехватка видеопамяти (VRAM)» - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: На ПК графический драйвер сохраняет однажды собранный шейдер в кэш шейдеров и потом использует его повторно. Поэтому сразу после обновления графического драйвера или выхода патча игры этот кэш становится недействительным, и даже у тех, у кого всё было нормально, какое-то время снова бывают рывки. Типичная жалоба: «после патча дёргается в каждом новом месте». - Источники: - [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) в момент, когда оно понадобилось, может занять больше 100 ms, поэтому PSO нужно создавать заранее - [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 во время игры дольше 20 ms (порог по умолчанию) считается рывком (hitch) #### cg-asset-stream · Стриминг ассетов отстаёт из-за медленного накопителя · Slow storage stalls asset streaming На медленном накопителе вроде HDD текстуры и модели открытого мира читаются медленнее, чем перемещается игрок. Объекты появляются с опозданием, или игра дёргается, ожидая окончания чтения. - Почему → Следствие → На экране: Быстрое перемещение на маунте или телепортом либо вход в людное место, где сразу нужно много новых текстур и моделей → Медленный накопитель вроде HDD не успевает читать с нужной скоростью, запросы на чтение копятся, а завершения некоторых загрузок главный поток ждёт → Текстуры какое-то время размытые, здания и персонажи появляются с опозданием, а пока игра ждёт чтения, бывают микрофризы и фризы - Симптомы: Невидимки / фантомы, Микрофризы, Фриз / Факторы: Остановка - У кого: Только у меня / Когда: В движении и при смене локации, При наплыве игроков - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Внешние стороны · Внешние стороны - Команда разработки, задачи: Стримить асинхронно, чтобы главный поток не ждал чтения, читать заранее с учётом направления и скорости движения, сначала показывать низкое разрешение (мип-уровни) и упрощённые модели, а потом подменять их, при отставании стриминга во время быстрого перемещения ненадолго ограничивать скорость или показывать экран загрузки, указать в минимальных и рекомендуемых требованиях, нужен ли SSD. - Внешние стороны, задачи: Посоветовать игрокам ставить игру на SSD и проверить, не идут ли на том же диске загрузки или антивирусная проверка. - Цифры для ориентира: По данным Microsoft, старые жёсткие диски читают десятки MB в секунду, NVMe SSD несколько GB в секунду, а игры прошлого поколения тратили на стриминг около 50 MB в секунду. Если игра рассчитана на скорость SSD и читает гораздо больше, на HDD чтение легко отстаёт от перемещения. - На графике: Высоко только у некоторых (число всплесков времени кадра (по типу накопителя), задержка чтения с диска) - Где смотреть: Во время быстрого перемещения записывать вместе счётчики PhysicalDisk\Avg. Disk sec/Read (среднее время одного чтения) и Current Disk Queue Length в Системном мониторе Windows и время кадра в PresentMon. В development-сборке смотреть stat Streaming и stat AsyncLoad в Unreal, в профайлере Unity предупреждения AssetBundle.asset/allAssets (результат запрошен до окончания загрузки, и главный поток ждёт) - Подтверждает: При быстром перемещении задержка чтения и очередь диска резко растут, и в те же моменты подскакивает время кадра или с опозданием появляются текстуры и объекты. На SSD в той же сцене этого нет - Опровергает: Если диск простаивает, а текстуры размытые, это «Нехватка видеопамяти (VRAM)». Если со второго посещения того же места всё нормально, это «Синхронная загрузка и компиляция шейдеров в главном потоке» - Чем проверить: Проверка на стороне игрока - Подробнее: Если фриз бывает только в первый раз, а дальше всё нормально, это ближе к причине «Синхронная загрузка и компиляция шейдеров в главном потоке». Если он повторяется при каждом перемещении и только на ПК с медленным накопителем, причина именно эта. Размытые текстуры даёт и другая причина, «Нехватка видеопамяти (VRAM)»: текстуры выгружаются из видеопамяти и загружаются обратно. Поэтому ожидание чтения с диска и занятость видеопамяти смотрят вместе. Чтение может замедлять и антивирус: проверка в реальном времени вмешивается каждый раз, когда игра открывает файл. - Источники: - [DirectStorage is coming to PC](https://devblogs.microsoft.com/directx/directstorage-is-coming-to-pc/) · Microsoft · Старые жёсткие диски читают десятки MB в секунду, NVMe SSD несколько GB в секунду, бюджет стриминга ассетов в играх прошлого поколения около 50 MB в секунду. Игры с открытым миром на ходу подгружают и выгружают дальние пейзажи - [Texture and mesh loading](https://docs.unity3d.com/Manual/LoadingTextureandMeshData.html) · Unity · Синхронная загрузка читает данные и передаёт их на GPU в главном потоке за один кадр, что даёт заметную остановку. Асинхронная загрузка стримит данные на протяжении нескольких кадров - [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: среднее время выполнения одного чтения (задержка 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 Когда в кадр попадают сотни игроков, как на осаде или у мирового босса, клиент не справляется уже с самой отрисовкой. - Почему → Следствие → На экране: В одном кадре сотни персонажей и наложенные друг на друга эффекты → Затраты на анимацию, тени, таблички с именами и эффекты растут пропорционально числу игроков → FPS падает с 60 до 15, микрофризы во всех движениях, ввод тоже запаздывает - Симптомы: Микрофризы, Задержка ввода / Факторы: Остановка - У кого: Одна локация или канал, Только у меня / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Упрощать по расстоянию (LOD), ограничить число отображаемых персонажей, добавить опцию упрощённых эффектов, реже обновлять анимацию. - Цифры для ориентира: Даже если на одного персонажа уходит 0,02–0,1 ms, на 300 персонажей это 6–30 ms. При 60 FPS бюджет кадра составляет 16,7 ms, так что уже одно это занимает больше 1/3 бюджета, а в худшем случае превышает его. - На графике: Растёт вслед за онлайном и нагрузкой (время кадра, число персонажей в кадре) - Где смотреть: Сравнить в PresentMon время кадра и CPUBusy, GPUBusy до и во время осады или боя с мировым боссом. В development-сборке 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 Если за кадр обрабатывается только фиксированное число полученных пакетов, при наплыве пакеты всё время переносятся на следующие кадры. - Почему → Следствие → На экране: В людном месте приходят тысячи обновлений в секунду → Главный поток упирается в лимит обработки на кадр и не успевает всё прочитать → Движения других игроков отображаются всё позже и пачками - Симптомы: Перемотка, Задержка ввода / Факторы: Остановка, Задержка - У кого: Одна локация или канал, Только у меня / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: принимать и разбирать пакеты в отдельном потоке, устаревшие обновления позиции одного объекта схлопывать и применять только последнее. Сервер: в людных местах реже отправлять обновления дальних персонажей, чтобы уменьшить объём трафика. - Цифры для ориентира: Когда необработанные пакеты копятся, отставание на целую секунду набегает за несколько секунд. - На графике: Растёт вслед за онлайном и нагрузкой (число необработанных полученных пакетов, задержка от приёма до применения) - Где смотреть: Писать в лог число пакетов, которые клиент не успел обработать за кадр, и задержку от прихода пакета до применения в игре, смотреть вместе с числом игроков поблизости - Подтверждает: В людных местах число необработанных пакетов и задержка применения постоянно растут, а пинг и интервал отправки с сервера в это время в норме - Опровергает: Если задержки применения нет, а сами пакеты приходят поздно, причина на участке сети. Если сильно растёт время кадра, это «Нагрузка на рендеринг при большом скоплении игроков» - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [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 Если отрисовывать пакеты сервера сразу после получения, джиттер (неравномерность интервалов между пакетами) целиком виден на экране. - Почему → Следствие → На экране: Полученная позиция рисуется сразу, или буфер короче джиттера → На опоздавших пакетах движение останавливается, на пришедших пачкой скачет вперёд → Другие персонажи двигаются рывками - Симптомы: Микрофризы / Факторы: Джиттер - У кого: Только у меня / Когда: Всегда - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: завести буфер интерполяции, автоматически подстраивать его длину под состояние подключения. Сервер: применять компенсацию задержки (проверку попадания с отмоткой времени назад), чтобы попадание засчитывалось правильно, даже если игрок стрелял по картинке, отстающей на длину буфера. - Цифры для ориентира: Обычно длину буфера берут примерно равной двум интервалам отправки пакетов сервером (при 20 пакетах в секунду это 100 ms). - На графике: Высоко с самого начала (интервал прихода пакетов, число опустошений буфера интерполяции) - Где смотреть: Записывать на клиенте распределение интервалов прихода пакетов сервера и число кадров, когда следующего снапшота для интерполяции не было и движение остановилось или перешло на экстраполяцию - Подтверждает: Разброс интервалов часто превышает длину буфера интерполяции, каждый раз буфер пустеет, и другие персонажи дёргаются. С более длинным буфером это случается реже - Опровергает: Если буфера достаточно, а рывки остаются, проверить, не сбивается ли сам интервал отправки на сервере (запаздывание тиков) - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Чем длиннее буфер, тем плавнее движение, но тем дальше в прошлом игрок видит противника. Поэтому вместе с буфером используют компенсацию задержки: при проверке попадания сервер отматывает время назад, к «прошлому, которое видел этот игрок». - Источники: - [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 · Чрезмерная экстраполяция (dead reckoning) · Over-extrapolation / dead reckoning Пока пакеты не приходят, клиент продолжает двигать объект с последней известной скоростью, а обнаружив ошибку, возвращает его назад. - Почему → Следствие → На экране: Пакеты перестали приходить, и объект продолжает двигаться с последним направлением и скоростью → На самом деле противник остановился или сменил направление → Персонаж противника долго идёт в одну сторону, а потом резко переносится в настоящую позицию или проходит сквозь стену. Если пакеты приходят неравномерно, он то убегает вперёд, то его тянет обратно, и движение выглядит дрожащим - Симптомы: Телепортация, Микрофризы / Факторы: Потери, Джиттер - У кого: Только у меня / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Ограничить время экстраполяции (например, 200–250 ms), при ошибке плавно сводить позицию к настоящей. - Цифры для ориентира: При скорости 6 m/s ошибка всего в 300 ms даёт расхождение 1,8 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 тиков, при 60 Hz около 1/3 с) - [Peeking into VALORANT's Netcode](https://technology.riotgames.com/news/peeking-valorants-netcode) · Riot Games · Если опоздавшие или потерянные данные восполняются догадкой и она неверна, картинка расходится с сервером, и персонаж при коррекции скачет или будто скользит #### cg-predict · Расхождение клиентского предсказания с сервером · Prediction mismatch / reconciliation Клиент заранее показал движение персонажа игрока, но сервер посчитал иначе, и персонажа утягивает на серверную позицию. - Почему → Следствие → На экране: Клиент двигает персонажа до подтверждения сервера (предсказание) → Сервер по-другому считает коллизии, скорость движения или баффы либо не получает команду → Когда приходит подтверждение, персонажа откидывает назад - Симптомы: Откидывание назад / Факторы: Потери, Задержка - У кого: Только у меня / Когда: В движении и при смене локации, Изредка, случайно - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: использовать тот же код движения, что и на сервере, отправлять ввод с дублированием, сглаживать коррекцию. Сервер: использовать тот же код движения, что и на клиенте, отсеивать дубли ввода по номеру и обрабатывать каждый ввод один раз. - Цифры для ориентира: Расстояние, на которое откидывает персонажа: «время расхождения × скорость движения». Потеря всего нескольких команд даёт 1–3 m. - На графике: Случайные всплески (число серверных коррекций (ошибок предсказания)) - Где смотреть: Записывать число коррекций позиции, отправленных сервером, и расстояние коррекции. В Unreal считать коррекции ClientAdjustPosition от сервера, в Unity Netcode for Entities считать, сколько раз из-за ошибки предсказания состояние возвращалось назад и пересчитывалось - Подтверждает: Коррекции скапливаются в моменты жалоб на откидывание назад, а с определёнными баффами, на определённых участках ландшафта или при умениях перемещения коррекция раз за разом получается большой - Опровергает: Если коррекции скапливаются только при больших потерях, дело в потере пакетов ввода (подключение). Если коррекций нет, а утягивает только других персонажей, это «Чрезмерная экстраполяция (dead reckoning)» - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [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 После одной остановки игра разом досчитывает накопившиеся шаги и из-за этого снова отстаёт. - Почему → Следствие → На экране: Симуляция идёт с фиксированным шагом, и игра один раз останавливается → Накопившиеся шаги считаются разом в одном кадре → Долгие кадры идут один за другим, и картинка скачет, или срабатывает лимит, и мир замедляется - Симптомы: Микрофризы, Перемотка, Слоумо / Факторы: Остановка - У кого: Только у меня / Когда: Изредка, случайно, При наплыве игроков - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Ограничить число догоняющих шагов за кадр, остаток времени закрывать интерполяцией. - На графике: Случайные всплески (время кадра, число фиксированных шагов за кадр) - Где смотреть: В профайлере development-сборки смотреть вместе время кадра и число фиксированных шагов за кадр (в Unity число маркеров фазы FixedUpdate, например FixedBehaviourUpdate) - Подтверждает: После одного долгого кадра идёт череда долгих кадров с несколькими шагами в каждом, а при достижении лимита (Maximum Allowed Timestep в Unity) игровое время течёт медленнее реального - Опровергает: Если долгий кадр одиночный, это «Всплески времени кадра» или «Сборка мусора на клиенте» - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Типичный пример фиксированного шага: физика Unity (FixedUpdate), по умолчанию 0,02 с, то есть 50 раз в секунду. Верхний предел для догоняющих шагов задаёт параметр Maximum Allowed Timestep в настройках Time (максимальное время, которое можно догнать за один кадр, по умолчанию около 0,33 с). Если кадр длиннее, лишнее время отбрасывается, и игровые часы на столько же отстают от реальных. - Источники: - [Set fixed timestep to optimize physics simulation frequency](https://docs.unity3d.com/Manual/physics-optimization-cpu-frequency.html) · Unity · Fixed Timestep по умолчанию 0,02 с (50 раз в секунду). Если кадр долгий, за один кадр выполняется несколько шагов физики и нагрузка растёт - [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 Если клиент неверно оценивает серверное время, сбиваются момент интерполяции и проверка кулдаунов. - Почему → Следствие → На экране: Время сервера синхронизируется один раз при входе и не корректируется, даже когда меняется пинг → Момент интерполяции и время окончания кулдауна расходятся с сервером → Противник иногда дёргается, умение отклоняется, хотя кулдаун уже прошёл - Симптомы: Микрофризы, Съеденные действия / роллбэк / Факторы: Задержка - У кого: Только у меня / Когда: Чем дольше без перезапуска, Изредка, случайно - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Регулярно синхронизировать время (измерять время пути туда и обратно и корректировать), подводить часы постепенно, без резких скачков, измерять прошедшее время монотонными часами (monotonic clock) вместо системного времени ПК. - На графике: Плавный рост (ошибка оценки серверного времени) - Где смотреть: Регулярно записывать разницу между серверным временем, которое оценил клиент, и серверным временем (номером тика), которое сервер прислал в пакете - Подтверждает: Ошибка растёт со временем после входа или скачком меняется в момент подводки часов ПК, и примерно тогда же растёт число жалоб на отклонённые умения и рывки - Опровергает: Если ошибка остаётся небольшой, а умения всё равно отклоняются, дело в решении сервера или задержке - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Если прошедшее время измерять по дате и времени ПК (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 · Расчёт задержки туда и обратно и расхождения часов по четырём отметкам времени запроса и ответа - [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 значащих цифр, поэтому через сутки работы (около 86 400 с) разрешение времени становится около 8 ms, то есть примерно половиной кадра при 60 FPS (16,7 ms), а через неделю около 60 ms, что уже больше одного кадра. - На графике: Плавный рост (жалобы на дрожание в зависимости от времени работы клиента) - Где смотреть: Вместе с жалобой на дрожание узнавать, сколько клиент работал без перезапуска, и сравнивать до и после перезапуска. На стороне разработки: тест, в котором время старта игры выставлено на несколько дней назад - Подтверждает: Дрожание есть только в клиентах, проработавших несколько дней, пропадает после перезапуска и тем сильнее, чем дольше работает клиент - Опровергает: Если дрожит сразу после запуска, это «Буфер интерполяции отсутствует или слишком короткий» или «Разрешение таймера» - Чем проверить: Проверка на стороне игрока - Подробнее: Особенно часто встречается в мобильных MMO, где игроки включают автобой и не выключают игру по несколько дней. Time.time в Unity тоже float, поэтому в Unity есть отдельное значение Time.timeAsDouble в double, и рекомендуется использовать его. - Источники: - [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 кадра → Ввод доходит до экрана на столько же позже → Пинг низкий, а управление ватное и запаздывает - Симптомы: Задержка ввода, Микрофризы / Факторы: Задержка - У кого: Только у меня / Когда: Всегда - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Внешние стороны · Внешние стороны - Команда разработки, задачи: Поддержать режим низкой задержки, сократить очередь кадров, дать опцию ограничения FPS чуть ниже частоты обновления, на телефонах включить frame pacing. - Внешние стороны, задачи: Посоветовать игрокам с монитором с переменной частотой обновления ставить ограничение FPS чуть ниже частоты обновления и включить режим низкой задержки в графическом драйвере. - Цифры для ориентира: При 60 Hz каждый кадр в очереди добавляет 16,7 ms. Если CPU работает быстрее GPU или частоты экрана и очередь из трёх кадров (по умолчанию в DirectX 11) заполнена, добавляется 50 ms. При V-Sync с двойной буферизацией кадр, на который ушло 17 ms, ждёт следующего обновления экрана (33,3 ms), а пока на экране ещё раз показывается предыдущий кадр. - На графике: Высоко с самого начала (задержка от ввода до экрана) - Где смотреть: Сравнивать в PresentMon MsClickToPhotonLatency и MsAllInputToPhotonLatency (от ввода мышью или клавиатурой до вывода на экран) и DisplayLatency, переключая V-Sync, режим низкой задержки и ограничение FPS. MsPCLatency (от получения ввода ПК до отправки на экран) записывается, только если игра отправляет события PC Latency - Подтверждает: С включённым V-Sync или без ограничения FPS эта задержка растёт на один-два кадра (десятки ms), а в режиме низкой задержки или с ограничением чуть ниже частоты обновления уменьшается. Пинг не меняется - Опровергает: Если задержка внутри ПК низкая, а управление всё равно запаздывает, это «Задержка дисплея, устройств ввода и генерации кадров». Если высокий пинг, дело в сети - Чем проверить: Проверка на стороне игрока - Подробнее: V-Sync (вертикальная синхронизация) выводит новый кадр только в момент, когда монитор обновляет изображение. Разрывы картинки пропадают, но ввод запаздывает на время ожидания этого момента, а если FPS опускается ниже 60, частота скачет между 60 и 30 и появляются микрофризы. Монитор с переменной частотой обновления подстраивает обновление под готовность кадра и сокращает это ожидание. На телефонах происходит то же самое. Если игра на 30 FPS не может равномерно выводить кадры на экран 60 Hz, в среднем выходит 30 FPS, но кадры держатся на экране неравномерно, например 49, 16 и 33 ms, и появляются микрофризы (пример из документации Android для разработчиков). Помогает библиотека Frame Pacing для 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 блокируется, пока очередь не освободится, и от отрисовки до показа проходит почти на кадр больше. Сократить это помогает swap chain с ожиданием (waitable swap chain) - [Frame Pacing library](https://developer.android.com/games/sdk/frame-pacing) · Android (Google) · На экране 60 Hz при отсутствии нового кадра снова показывается предыдущий. Пример игры на 30 FPS, у которой время кадра скачет: 49, 16, 33 ms - [PresentMon Capture Application (README-CaptureApplication.md)](https://raw.githubusercontent.com/GameTechDev/PresentMon/main/README-CaptureApplication.md) · Intel · MsPCLatency (от получения ввода ПК до отправки на экран), 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 запускается чаще, памяти ОС не хватает, начинается своп → Через несколько часов игры микрофризы нарастают, потом игра принудительно закрывается (игроку это кажется дисконнектом) - Симптомы: Микрофризы, Дисконнект / Факторы: Остановка - У кого: Только у меня / Когда: Чем дольше без перезапуска - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Замерять расход памяти при смене локаций, находить и исправлять неосвобождаемые текстуры, UI и эффекты, гонять долгие автотесты (soak-тесты). - На графике: Плавный рост (память процесса игры) - Где смотреть: Несколько часов записывать в Системном мониторе счётчик Process(игра)\Private Bytes. На мобильных смотреть причину завершения в ApplicationExitInfo на Android (REASON_LOW_MEMORY) и отчёты jetsam на iOS - Подтверждает: С каждым переходом между локациями память растёт и не возвращается, и чем дольше работает игра, тем больше микрофризов и принудительных закрытий - Опровергает: Если память стабильна, а с долгой работой появляется только дрожание, это «Потеря точности времени во float» - Чем проверить: Проверка на стороне игрока - Подробнее: Телефоны обычно держатся за счёт сжатия памяти. Если памяти всё равно не хватает, ОС сразу закрывает игру (вылет). Чем меньше ОЗУ у устройства, тем раньше это происходит. - Источники: - [Memory allocation among processes](https://developer.android.com/topic/performance/memory-management) · Android (Google) · Android держится за счёт сжатия памяти в zRAM, а когда памяти не хватает, low memory killer завершает процессы. Если завершено приложение на переднем плане, это выглядит как краш - [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: процесс приложения завершён системным low memory killer (устройства без поддержки сообщают REASON_SIGNALED и SIGKILL) #### cg-crash · Краш клиента · Client crash Игра закрывается из-за необработанной ошибки. Игроку это кажется дисконнектом, хотя сервер работает нормально. - Почему → Следствие → На экране: Обращение по нулевой ссылке, нехватка памяти, ошибка графического драйвера → Процесс игры принудительно завершается → Жалобы «вылетел из игры». У остальных в это же время всё в порядке - Симптомы: Дисконнект / Факторы: Остановка - У кого: Только у меня / Когда: При определённом действии, Изредка, случайно - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Внешние стороны · Внешние стороны - Команда разработки, задачи: Собирать краш-репорты, вести статистику по устройствам и драйверам, исправлять сначала самые частые ошибки. - Внешние стороны, задачи: Если краши скапливаются на определённой версии графического драйвера, посоветовать игрокам обновить драйвер. - На графике: Высоко только у некоторых (число крашей (по устройствам, графическим драйверам и сборкам)) - Где смотреть: Смотреть краш-репорты и частоту крашей в Android vitals по устройствам, драйверам и сборкам. На ПК игрока смотреть в Просмотре событий в журнале «Приложение» события с кодом 1000 (имя сбойного модуля) и записи «Display driver stopped responding and has recovered» - Подтверждает: На момент жалобы на дисконнект есть запись о краше, а другие игроки на том же сервере в это время играют нормально. Краши скапливаются на определённых устройствах, версиях драйвера или модулях - Опровергает: Если записи о краше нет и просто оборвалось соединение, это «Истечение записи в таблице NAT» или проблема с подключением - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [Crashes](https://developer.android.com/topic/performance/vitals/crash) · Android (Google) · Краш: неожиданное завершение приложения из-за необработанного исключения или сигнала (SIGSEGV и т. п.), статистика собирается в Android vitals в Play Console - [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 · Событие с кодом 1000 в журнале «Приложение» и есть запись о краше, в нём указаны имя сбойного приложения и сбойного модуля (Faulting module name) #### cg-anticheat · Проверки модуля защиты игры (античита) · Anti-cheat scan and heartbeat Модуль защиты от взлома работает вместе с игрой и периодически выполняет проверки. Если проверка тяжёлая или хартбит (периодический сигнал «я жив») с сервером защиты запаздывает, появляются микрофризы или дисконнект. - Почему → Следствие → На экране: Модуль защиты периодически проверяет память игры, запущенные программы и драйверы → Во время проверки игровой поток стоит, или хартбит не уходит вовремя → Рывки через равные промежутки, в тяжёлых случаях дисконнект с сообщением об ошибке защиты - Симптомы: Микрофризы, Фриз, Дисконнект / Факторы: Остановка - У кого: Только у меня / Когда: С постоянным периодом, Сразу после входа или техработ, Изредка, случайно - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: выполнять тяжёлые проверки вне игрового потока небольшими порциями, сравнивать статистику микрофризов и вылетов по версиям модуля защиты (если они скапливаются сразу после обновления, сообщить разработчику модуля). Сервер: допускать один-два опоздавших хартбита. - Цифры для ориентира: Лёгкая проверка обычно занимает меньше 1 ms, а тяжёлая проверка в игровом потоке, в зависимости от реализации, может забирать за раз от десятков до сотен ms. - На графике: Всплески с постоянным периодом (время кадра, число киков античитом) - Где смотреть: Измерить интервал между всплесками времени кадра в PresentMon и собрать причины киков античитом, полученные сервером (в EOS это AuthenticationFailed / Authentication Timed Out и др. в ClientActionReason), по версиям модуля защиты и конфигурациям - Подтверждает: Короткие остановки повторяются через равные промежутки независимо от происходящего в игре, а сразу после обновления модуля защиты на определённых конфигурациях растут микрофризы и кики по таймауту аутентификации - Опровергает: Если интервал одинаковый на всех конфигурациях и не зависит от версии модуля защиты, это «Сборка мусора на клиенте» или «Фоновые процессы занимают CPU» - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Модуль защиты встроен глубоко в ОС в виде драйвера и может конфликтовать с антивирусом, оверлеями и модулями защиты других игр. Если сразу после обновления модуля защиты жалобы на микрофризы и вылеты скапливаются на определённых конфигурациях, его стоит заподозрить первым. - Источники: - [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 ОС и устройство клиента (причин: 15) #### co-background · Фоновые процессы занимают CPU · Background CPU contention Когда ядра заняты антивирусной проверкой, обновлением Windows, программой для стримов или видео в браузере, игровой поток не получает процессорного времени и ждёт. - Почему → Следствие → На экране: Другие программы надолго занимают ядра CPU → Игровой поток ждёт, пока планировщик выделит ему ядро → Кадры запаздывают, обработка полученных пакетов тоже - Симптомы: Микрофризы, Перемотка / Факторы: Остановка - У кого: Только у меня / Когда: Изредка, случайно, С постоянным периодом - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Настроить приоритет игровых потоков, писать в лог, снятый в момент микрофризов, общую загрузку CPU на ПК, чтобы отличать случаи, когда виноваты другие программы. - Внешние стороны, задачи: Посоветовать игрокам включить игровой режим Windows и закрывать во время игры ненужные программы (антивирусную проверку, обновление Windows, программу для стримов, видео в браузере). - Цифры для ориентира: Windows обычно выделяет потоку ядро на срок от единиц до десятков ms за раз. Если планировщик хотя бы раз отложит игровой поток, кадр потерян. - На графике: Случайные всплески (общая загрузка 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 выделяет каждому потоку квант времени и по его окончании переключается на следующий поток, квант около 20 ms (зависит от ОС и 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 Из-за работы ноутбука от батареи, режима энергосбережения на телефоне или нагрева устройства падает скорость CPU и GPU. Характерный признак перегрева: сначала всё нормально, а тормоза начинаются заметно позже. - Почему → Следствие → На экране: Устройство работает от батареи или в режиме энергосбережения либо нагрелось → Частоты CPU и GPU снижаются на 30–50% в зависимости от устройства → В режиме энергосбережения сразу, а при перегреве спустя от нескольких минут до примерно 20 минут игры FPS падает и появляются микрофризы - Симптомы: Микрофризы, Задержка ввода / Факторы: Остановка - У кого: Только у меня / Когда: Чем дольше без перезапуска, Всегда - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Автоматически подстраивать графические настройки, сдерживать нагрев ограничением FPS, заранее снижать настройки по уровню нагрева, который сообщает ОС (thermalState в iOS, API теплового состояния в Android), на ноутбуках с двумя графическими чипами пометить исполняемый файл, чтобы использовалась дискретная графика (экспортировать NvOptimusEnablement и AmdPowerXpressRequestHighPerformance). - Внешние стороны, задачи: Посоветовать игрокам выключить энергосбережение, а ноутбук держать подключённым к сети. На жалобы «ноутбук мощный, а FPS низкий» попросить проверить, на каком графическом чипе работает игра, и назначить для игры высокопроизводительный GPU в настройках графики Windows. - На графике: Плавный рост (FPS, частоты CPU и GPU) - Где смотреть: Записывать в PresentMon CPUFrequency, GPUFrequency, CPUTemperature и GPUTemperature вместе со временем кадра в течение 20–30 минут, а по столбцу «Ядро ГП» на вкладке «Процессы» Диспетчера задач проверить, на каком графическом чипе работает игра. На мобильных записывать вместе с FPS тепловой API Android (getThermalHeadroom, тепловое состояние) и thermalState в iOS - Подтверждает: FPS падает с того момента, когда после роста температуры снижаются частоты, или частоты низкие только от батареи и в режиме энергосбережения. Либо игра работает на встроенной графике - Опровергает: Если частоты и температура не меняются, а FPS падает, это «Фоновые процессы занимают CPU» или «Утечка памяти на клиенте» - Чем проверить: Проверка на стороне игрока - Подробнее: Ноутбуки с двумя графическими чипами ради экономии энергии иногда запускают игру на медленной встроенной графике. На жалобу «ноутбук мощный, а FPS низкий» первым делом проверяют, на каком графическом чипе работает игра. - Источники: - [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 · На ноутбуке с двумя графическими чипами игра на встроенной графике может выдавать 30 FPS вместо 60 FPS. Экспорт AmdPowerXpressRequestHighPerformance выбирает дискретную графику - [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,6 ms, поэтому «подождать всего 1 ms» на деле растягивается до следующего тика таймера, то есть до 15,6 ms. - Почему → Следствие → На экране: Ограничение FPS и отправка пакетов сделаны через Sleep (короткое ожидание) → ОС будит поток только с шагом 15,6 ms → Интервалы между кадрами и между отправками ввода неравномерные - Симптомы: Микрофризы / Факторы: Джиттер - У кого: Только у меня / Когда: Всегда - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Использовать таймеры высокого разрешения, выдерживать интервалы кадров по событиям или V-Sync вместо Sleep. - Цифры для ориентира: С шагом 15,6 ms интервал 16,7 ms выдержать нельзя, поэтому интервал между кадрами прыгает между 15,6 ms и 31,2 ms. - На графике: Высоко с самого начала (распределение интервалов между кадрами) - Где смотреть: Смотреть распределение MsBetweenPresents (интервал между кадрами) в PresentMon и пункт «Platform Timer Resolution» в отчёте powercfg /energy (процессы, изменившие разрешение таймера) - Подтверждает: Интервалы между кадрами скапливаются на значениях, кратных 15,6 ms (15,6 ms и 31,2 ms), и игра не запрашивает более высокое разрешение таймера - Опровергает: Если интервалы разбросаны равномерно, дело скорее в причине «Фоновые процессы занимают CPU» или в тяжёлых кадрах, чем в таймере - Чем проверить: Проверка на стороне игрока - Подробнее: В старых версиях Windows, если одна программа переключала таймер на 1 ms, это действовало на все программы. Отсюда и поверье «с открытым браузером игра идёт плавнее». Начиная с 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,6 ms, у таймера высокого разрешения 1 ms - [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,6 ms, процессы, изменившие его, видны в пункте «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 Если ненадолго свернуть игру, чтобы посмотреть уведомление, через несколько секунд ОС приостанавливает приложение (suspend), а сервер тем временем отключает игрока. - Почему → Следствие → На экране: Игру свернули, чтобы прочитать сообщение или ответить на звонок → Движок останавливает игровой цикл, а вскоре ОС приостанавливает и само приложение, и его сетевую активность → По возвращении уже дисконнект и переподключение - Симптомы: Дисконнект / Факторы: Остановка, Потери - У кого: Только у меня / Когда: После бездействия, При определённом действии - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: по возвращении сразу автоматически переподключаться по токену сессии, не дожидаясь старого соединения (продолжать без повторного входа), и разом получать актуальное состояние. Сервер: когда хартбиты прекращаются, закрывать соединение, но ещё недолго сохранять сессию персонажа (окно переподключения, без немедленного кика) и при переподключении в это окно подхватывать её по токену сессии. - Цифры для ориентира: Движок обычно останавливается в момент сворачивания. iOS приостанавливает приложение через несколько секунд, а если оно запросило дополнительное время, обычно в пределах десятков секунд. Android 14 и новее замораживает (freeze) ушедшее с экрана приложение примерно через 10 секунд. - На графике: Массовый обрыв соединений (число обрывов (таймаут хартбита), записи о приостановке приложения) - Где смотреть: Сопоставить по ID сессии время приостановки и возврата приложения из лога клиента (в Unity OnApplicationPause) с причиной и временем обрыва на сервере. На 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: процесс приложения завершён системным low memory killer (устройства без поддержки сообщают 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 Когда антивирус или файрвол проверяет каждый пакет, задержка растёт, а при слишком строгих правилах игра принимается за атаку и блокируется. - Почему → Следствие → На экране: Защитная программа проверяет каждый входящий и исходящий пакет → К каждому пакету добавляется задержка, а если проверка не успевает, пакеты отбрасываются → Пинг беспорядочно скачет, или подключение блокируется - Симптомы: Микрофризы, Ошибка входа / бесконечная загрузка / Факторы: Джиттер, Потери - У кого: Только у меня / Когда: Всегда, Сразу после входа или техработ - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Вести список совместимости с защитными программами, при установке добавлять игру в исключения брандмауэра Windows. - Внешние стороны, задачи: Посоветовать игрокам добавить игру в исключения защитной программы. Если программа принимает игру за атаку, попросить её разработчика исправить ложное срабатывание. - Цифры для ориентира: В норме проверка пакета занимает меньше 1 ms. Проблемы начинаются, когда модуль проверки не успевает, содержит ошибку или принимает игровой трафик за атаку. - На графике: Высоко только у некоторых (RTT и ошибки подключения (по игрокам)) - Где смотреть: Сравнить, ненадолго выключив защитную программу или добавив игру в исключения. В Windows, если в политике аудита включить Audit Filtering Platform Connection и Audit Filtering Platform Packet Drop, в журнале безопасности появляются события 5157 (соединение заблокировано) и 5152 (пакет заблокирован), а число отброшенных пакетов видно в Системном мониторе по счётчику WFPv4\Packets Discarded/sec - Подтверждает: Есть записи о блокировке соединений или пакетов к адресу игрового сервера, или с выключенной защитной программой скачки пинга и ошибки входа пропадают - Опровергает: Если то же самое на других устройствах в том же доме независимо от защитной программы, дело в роутере или подключении - Чем проверить: Проверка на стороне игрока - Источники: - [About Windows Filtering Platform](https://learn.microsoft.com/en-us/windows/win32/fwp/about-windows-filtering-platform) · Microsoft · Архитектура, в которой хуки сетевого стека Windows и движок фильтрации пропускают или блокируют пакеты. Сторонние разработчики защитных программ могут встраивать свои модули фильтрации (callout) - [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 · Счётчик Packets Discarded/sec в WFPv4 и WFPv6 #### co-rcvbuf · Переполнение буфера приёма · Socket receive buffer overflow Если игра занята и поздно забирает пакеты из сокета (интерфейса ОС для приёма и отправки данных по сети), буфер ОС переполняется. - Почему → Следствие → На экране: Кадры запаздывают, и игра поздно читает сокет → Буфер приёма ОС заполняется: UDP-пакеты отбрасываются, а TCP уменьшает окно приёма и заставляет отправителя остановиться → Телепортация (UDP) или перемотка (TCP) - Симптомы: Телепортация, Перемотка / Факторы: Потери, Остановка - У кого: Только у меня / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Вынести приём в отдельный поток, подобрать размер буфера (SO_RCVBUF). - Цифры для ориентира: Буфер приёма по умолчанию, в зависимости от ОС и настроек, от десятков до сотен KB. Обновления в людном месте могут достигать сотен 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 · SO_RCVBUF в Windows: буферное пространство, которое резервируется для приёма в каждом сокете - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Поле окна TCP показывает, сколько ещё байт готов принять получатель. При 0 отправитель только шлёт пробы нулевого окна (zero window probe) и ждёт - [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 · Dropped Datagrams и Dropped Datagrams/sec из набора счётчиков Microsoft Winsock BSP: число 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 Если вместе с игрой открыты десятки вкладок браузера, ОС выгружает часть памяти игры на диск. - Почему → Следствие → На экране: Оперативной памяти в целом не хватает → ОС переносит на диск память игры, которая сейчас не используется → Когда игра снова обращается к этой памяти, фриз на десятки или сотни ms в зависимости от накопителя - Симптомы: Фриз, Микрофризы / Факторы: Остановка - У кого: Только у меня / Когда: В движении и при смене локации, Изредка, случайно - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сокращать расход памяти, предупреждать игрока, когда свободной памяти мало. - Внешние стороны, задачи: Сообщить игрокам минимальные требования, посоветовать закрывать во время игры другие программы (вкладки браузера и т. п.). - На графике: Случайные всплески (жёсткие ошибки страниц, расход памяти) - Где смотреть: Записывать вместе со временем кадра счётчик Memory\Pages Input/sec в Системном мониторе (число страниц, прочитанных с диска для обработки жёстких ошибок страниц) и на вкладке «Производительность» Диспетчера задач использование памяти и значение «Выделено» (commit) - Подтверждает: В моменты фризов 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 · Файл подкачки: файл на диске, в который из ОЗУ выгружаются изменённые и редко используемые страницы памяти - [Working Set](https://learn.microsoft.com/en-us/windows/win32/memory/working-set) · Microsoft · Обращение к странице, которой нет в ОЗУ, вызывает ошибку страницы. Жёсткая ошибка устраняется только чтением с диска, например из файла подкачки - [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 Если графическим настройкам нужно больше памяти, чем есть на видеокарте, ОС выгружает текстуры в оперативную память ПК и загружает обратно, и появляются микрофризы. - Почему → Следствие → На экране: Высокое качество текстур, множество разного снаряжения и эффектов в людном месте забивают память видеокарты → ОС переносит неиспользуемые сейчас текстуры в оперативную память ПК, а когда они нужны, возвращает их по медленной шине PCIe → Рывки при каждом появлении новой сцены или нового персонажа, текстуры какое-то время размытые - Симптомы: Микрофризы, Фриз / Факторы: Остановка - У кого: Только у меня / Когда: При наплыве игроков, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Внешние стороны · Внешние стороны - Команда разработки, задачи: Выбирать настройки по умолчанию под объём видеопамяти, при выходе за бюджет памяти автоматически снижать качество текстур, в людных местах упрощать текстуры персонажей. - Внешние стороны, задачи: Посоветовать игрокам снизить качество текстур, а при запуске двух клиентов снизить настройки ещё сильнее. - Цифры для ориентира: Память видеокарты читает сотни GB в секунду, а шина PCIe между ней и оперативной памятью ПК, в зависимости от поколения, передаёт около 16–64 GB в секунду, то есть более чем в десять раз медленнее. - На графике: Упор в лимит (плато) (выделенная память GPU, общая память GPU) - Где смотреть: Смотреть графики выделенной и общей памяти графического процессора в разделе GPU на вкладке «Производительность» Диспетчера задач (на вкладку «Подробности» можно добавить такие же столбцы по процессам) вместе со временем кадра в PresentMon - Подтверждает: Пока выделенная память GPU упирается в предел и график становится плоским, а общая растёт, рывки частые. При снижении качества текстур они пропадают - Опровергает: Если выделенная память не заполнена, это «Стриминг ассетов отстаёт из-за медленного накопителя» или «Синхронная загрузка и компиляция шейдеров в главном потоке» - Чем проверить: Проверка на стороне игрока - Подробнее: Признак: в разделе GPU Диспетчера задач Windows «Выделенная память графического процессора» заполнена, а «Общая память графического процессора» растёт. Если на одном ПК запущены два клиента, память заполняется быстрее (см. причину «Сбой стриминга из-за нехватки памяти или VRAM»). - Источники: - [Residency](https://learn.microsoft.com/en-us/windows/win32/direct3d12/residency) · Microsoft · У процесса есть бюджет видеопамяти, и при его превышении ядро ОС переносит часть кучи дискретного GPU в оперативную память ПК (это крайняя мера, поэтому рекомендуется следить за бюджетом) - [GPUs in the task manager](https://devblogs.microsoft.com/directx/gpus-in-the-task-manager/) · Microsoft · Выделенная память GPU в Диспетчере задач означает VRAM видеокарты, общая память GPU означает оперативную память ПК, которую делят GPU и CPU - [CUDA C++ Best Practices Guide](https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/index.html) · NVIDIA · Пропускная способность видеопамяти (V100: 898 GB/s) намного выше, чем у PCIe x16 3-го поколения (16 GB/s), поэтому рекомендуется сокращать обмен с оперативной памятью ПК - [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 ОС периодически перебирает радиоканалы в поисках соседних сетей Wi-Fi, и на это время связь ненадолго прерывается. - Почему → Следствие → На экране: ОС или драйвер через равные промежутки ищут окружающие сети Wi-Fi → На время поиска приём и передача ненадолго останавливаются → Пинг скачет строго через равные промежутки (например, каждые 60 секунд) - Симптомы: Микрофризы, Телепортация / Факторы: Джиттер - У кого: Только у меня / Когда: С постоянным периодом - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Во время игры запрашивать режим, который сокращает сканирование (на Android режим Wi-Fi с низкой задержкой WIFI_MODE_FULL_LOW_LATENCY, в Windows режим потоковой передачи мультимедиа через WlanSetInterface; на некоторых устройствах и драйверах это не помогает). - Внешние стороны, задачи: Посоветовать игрокам кабельное подключение, настройку служб геолокации и автоматического поиска Wi-Fi, обновление драйвера беспроводного адаптера. - Цифры для ориентира: Обычно от десятков до сотен ms за раз. Если всплески подозрительно регулярные, эту причину проверяют первой. - На графике: Всплески с постоянным периодом (RTT до роутера) - Где смотреть: Во время игры несколько минут пинговать адрес роутера (основной шлюз в выводе ipconfig) командой ping /t и измерить интервал между всплесками. Повторить то же по кабелю - Подтверждает: Пинг до роутера строго через равные промежутки (например, 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 · API Windows, который включает и выключает фоновое сканирование (wlan_intf_opcode_background_scan_enabled) и режим потоковой передачи мультимедиа - [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 Если сетевая карта или модуль Wi-Fi уходят в энергосбережение между пакетами, на пробуждение нужно время. - Почему → Следствие → На экране: Включено энергосбережение сетевого адаптера, или драйвер устарел → Задержка выхода из энергосбережения (wake-up), иногда перезапуск устройства → Нерегулярные задержки, изредка фриз на несколько секунд - Симптомы: Микрофризы, Фриз / Факторы: Джиттер, Потери - У кого: Только у меня / Когда: После бездействия, Изредка, случайно - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: В клиенте для Android во время игры запрашивать режим Wi-Fi с низкой задержкой (WIFI_MODE_FULL_LOW_LATENCY), чтобы отключить энергосбережение Wi-Fi. - Внешние стороны, задачи: Посоветовать игрокам обновить сетевой драйвер и отключить энергосбережение сетевого адаптера в Диспетчере устройств, а если весь экран дёргается и звук хрипит, найти виновный драйвер с помощью LatencyMon. - На графике: Случайные всплески (время DPC и ISR, RTT до роутера) - Где смотреть: Записать трассу в Windows Performance Recorder (WPR), найти на графике DPC/ISR в Windows Performance Analyzer (WPA) драйверы с долгим выполнением (столбец Module) и проверить в Диспетчере устройств настройки управления электропитанием сетевого адаптера - Подтверждает: В моменты рывков DPC и ISR сетевого драйвера длятся по несколько ms, или после отключения энергосбережения нерегулярные задержки пропадают - Опровергает: Если DPC короткие, а с отключённым энергосбережением ничего не меняется, это «Помехи и слабый сигнал Wi-Fi» или «Фоновое сканирование Wi-Fi» - Чем проверить: Проверка на стороне игрока - Подробнее: Если драйвер долго занимает CPU обработкой прерываний (в Windows это называют задержкой DPC), игровой поток всё это время тоже не может использовать ядро. Тогда при низкой загрузке CPU весь экран дёргается, а звук хрипит. Найти виновный драйвер помогают инструменты вроде LatencyMon, и чаще всего это драйверы Wi-Fi или сетевой карты. - Источники: - [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, все потоки на этом ядре стоят, поэтому рекомендуется не превышать 100 µs за раз - [Wi-Fi low-latency mode](https://source.android.com/docs/core/connect/wifi-low-latency) · Android (Google) · В режиме Wi-Fi с низкой задержкой фреймворк Android явно отключает энергосбережение Wi-Fi - [CPU Analysis](https://learn.microsoft.com/en-us/windows-hardware/test/wpt/cpu-analysis) · Microsoft · График DPC/ISR в WPA: длительность каждого непрерывного выполнения DPC или ISR и модуль (Module), в котором находится эта функция #### co-other-apps · Другие приложения на устройстве забирают пропускную способность · Other apps saturating the link Если на том же ПК идёт синхронизация с облаком, большая загрузка или скачивание патча игры, игровые пакеты ждут в очереди. - Почему → Следствие → На экране: Другие приложения загружают или отдают данные на максимальной скорости → Игровые пакеты копятся в очередях ПК и роутера → Пинг взлетает, задержка ввода, перемотка - Симптомы: Задержка ввода, Перемотка / Факторы: Задержка, Джиттер - У кого: Только у меня, Все в одном доме / Когда: Изредка, случайно - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Собственный лаунчер и патчер: во время игры приостанавливать фоновые загрузки или ограничивать их скорость. - Внешние стороны, задачи: Посоветовать игрокам ограничить скорость загрузок и отключать автообновления во время игры. - На графике: Растёт вслед за онлайном и нагрузкой (RTT, объём трафика ПК) - Где смотреть: Записывать в Системном мониторе Network Interface\Bytes Sent/sec и Bytes Received/sec вместе с пингом. Метод тот же, что в тесте на bufferbloat: держать пинг запущенным и намеренно запустить большую передачу данных - Подтверждает: Пока загрузка или отдача близка к скорости подключения, пинг растёт на десятки или сотни ms, а после остановки передачи сразу возвращается - Опровергает: Если трафик ПК низкий, а пинг растёт, это «Bufferbloat (очередь в роутере)» из-за другого устройства в доме или проблема на участке провайдера - Чем проверить: Проверка на стороне игрока - Источники: - [Introduction](https://www.bufferbloat.net/projects/bloat/wiki/Introduction/) · Bufferbloat.net · Если роутер или другое сетевое оборудование копит слишком много данных, задержка резко растёт (bufferbloat) - [Delivery Optimization reference](https://learn.microsoft.com/en-us/windows/deployment/do/waas-delivery-optimization-reference) · Microsoft · Загрузка обновлений Windows (оптимизация доставки) по умолчанию динамически подстраивается под доступную пропускную способность, а для фоновых и активных загрузок можно задать верхний предел пропускной способности - [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 · Если держать пинг запущенным, загрузить подключение тестом скорости и пинг вырастет, это bufferbloat #### co-unfocused · Ограничение обработки в свёрнутом или неактивном окне · Minimized / unfocused window throttling Когда игрок переключается на другое окно или сворачивает игру, и сама игра, и Windows ради экономии энергии замедляют её работу. По возвращении накопившиеся пакеты приходят разом, или соединение уже оборвано. - Почему → Следствие → На экране: Переключение на другое окно через Alt+Tab или сворачивание игры → Пока игру не видно, она сильно снижает FPS или останавливается, а Windows тоже понижает приоритет невидимых программ → В момент возвращения перемотка, а если игра была свёрнута долго, дисконнект - Симптомы: Перемотка, Микрофризы, Дисконнект / Факторы: Остановка - У кого: Только у меня / Когда: При определённом действии, После бездействия - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Продолжать приём пакетов и хартбиты в отдельном потоке, даже когда окно не видно, проверить настройку движка «Run In Background», при возвращении разом переходить к актуальному состоянию. - Цифры для ориентира: Если в невидимом окне FPS снижается до 5–10, один кадр длится 100–200 ms. Игра, которая обрабатывает пакеты раз в кадр, читает их с таким же опозданием. - На графике: Провал, затем пачка (интервал между кадрами (до и после переключения окна), число обработанных пакетов) - Где смотреть: С запущенным PresentMon переключиться через Alt+Tab или свернуть игру и посмотреть интервал между кадрами, пока окно не видно. Писать в лог игры моменты смены фокуса окна и сопоставлять их с причинами обрывов - Подтверждает: Пока окно не видно, интервал между кадрами растёт до 100 ms и больше или запись прерывается, а в момент возвращения накопившиеся пакеты обрабатываются разом, и получается перемотка. Если игра свёрнута долго, соединение рвётся по таймауту хартбита - Опровергает: Если то же самое и с открытым окном, это «Фоновые процессы занимают CPU» или проблема в сети - Чем проверить: Проверка на стороне игрока - Подробнее: Windows 11 не гарантирует таймер 1 ms программам, чьё окно свёрнуто или полностью перекрыто и не издаёт звука. На ноутбуке, работающем от батареи, такие программы переводятся на самую экономичную частоту, а на CPU с ядрами разных типов могут работать на медленных энергоэффективных ядрах. Если из двух клиентов на одном ПК странно ведёт себя только фоновый, посмотрите также причину «Ограничение обработки в фоновом окне». - Источники: - [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 в той же сцене, а при крашах посмотреть имя сбойного модуля (Faulting module name) в событии с кодом 1000 в Просмотре событий - Подтверждает: С выключенными оверлеями рывки и графические ошибки пропадают, или сбойным модулем в краше оказывается 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 · Событие с кодом 1000 в журнале «Приложение» содержит имя сбойного модуля (Faulting module name). Из-за повреждения, вызванного другим модулем, сбойным иногда указывается модуль Windows #### co-display-input · Задержка дисплея, устройств ввода и генерации кадров · Display, input device and frame generation latency Если пинг в норме, а управление ватное, задержку между вводом и экраном может добавлять обработка изображения в телевизоре, беспроводной контроллер или генерация кадров. - Почему → Следствие → На экране: Игровой режим телевизора выключен, используется Bluetooth- или другой беспроводной контроллер, или включена генерация кадров (DLSS Frame Generation, FSR Frame Generation) → Телевизор выводит кадр позже из-за обработки изображения, беспроводной ввод опаздывает на период опроса и из-за помех, а генерация кадров ждёт следующий кадр, чтобы построить промежуточный → Пинг и FPS хорошие, но нажатие доходит до экрана с опозданием: задержка ввода - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Только у меня / Когда: Всегда - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сделать генерацию кадров опцией и предупреждать, что с ней может вырасти задержка ввода, вместе с генерацией кадров интегрировать функции низкой задержки от производителей GPU (NVIDIA Reflex, AMD Anti-Lag 2), показывать в игре задержку от ввода до экрана на стороне ПК, в сборках для Android TV и приставок запрашивать у телевизора режим низкой задержки (ALLM) через Window.setPreferMinimalPostProcessing(true). - Внешние стороны, задачи: Посоветовать игрокам включить игровой режим (ALLM) на телевизоре или мониторе, в соревновательном контенте использовать проводной контроллер и выключать генерацию кадров, держать Bluetooth-устройства поближе, а Wi-Fi использовать в диапазоне 5 GHz. - Цифры для ориентира: Только на передачу одного кадра на экран 60 Hz уходит 16,7 ms, на 120 Hz 8,3 ms. Старые контроллеры Xbox опрашивали и отправляли ввод каждые 8 ms. Задержка от обработки изображения в телевизоре у всех моделей разная, и одной цифрой её не назвать. Игровой режим как раз сокращает эту обработку. AMD рекомендует использовать генерацию кадров, если исходная частота кадров не ниже 60 FPS. - На графике: Высоко с самого начала (задержка от ввода до экрана) - Где смотреть: Сравнить MsAllInputToPhotonLatency в PresentMon (от ввода с клавиатуры или мыши до вывода на экран) с включённой и выключенной генерацией кадров и по FrameType (записывается, только если его сообщают драйвер или SDK) посмотреть, есть ли среди кадров сгенерированные промежуточные. Беспроводной участок контроллера и обработка внутри телевизора в это значение не входят, поэтому эту часть проверять, включая игровой режим телевизора и меняя контроллер на проводной - Подтверждает: Пинг в норме, а с выключенной генерацией кадров задержка от ввода до экрана уменьшается, или с игровым режимом телевизора и проводным контроллером ощутимая задержка пропадает - Опровергает: Если после всех этих изменений ничего не меняется, а пинг высокий или скачет, дело в сети. Если задержка на стороне ПК высокая из-за V-Sync или очереди кадров, это «V-Sync и очередь рендеринга» - Чем проверить: Проверка на стороне игрока - Подробнее: Сетевую задержку видно по пингу, а эта задержка в пинг не попадает. Поэтому при жалобе «пинг низкий, а лагает» её проверяют первой. Генерация кадров примерно вдвое поднимает число FPS на экране, но для построения промежуточного кадра нужно дождаться следующего настоящего, поэтому ввод доходит до экрана дольше (AMD указывает, что рост задержки заложен в саму технологию). Bluetooth-устройства используют тот же диапазон 2,4 GHz, что и Wi-Fi, и при помехах ввод может прерываться или дёргаться. О 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 → телевизор. Старые контроллеры опрашивали и отправляли ввод каждые 8 ms. Передача одного кадра по HDMI занимает 16,6 ms при 60 Hz и 8,3 ms при 120 Hz. ALLM автоматически включает игровой режим телевизора - [AMD FSR 3 game integrations out now + more details for developers](https://gpuopen.com/news/fsr3-in-games-technical-details/) · AMD · Интерполяция кадров по своему устройству увеличивает задержку. Рекомендуется исходная частота от 60 кадров в секунду, из 60 FPS на входе получается до 120 FPS на выходе - [AMD FSR Frame Generation](https://gpuopen.com/amd-fsr-framegeneration/) · AMD · Для генерации кадров рекомендуется исходная частота от 60 FPS (ниже 30 FPS её следует избегать). AMD Radeon Anti-Lag 2 согласует работу CPU и GPU и снижает системную задержку - [NVIDIA DLSS](https://developer.nvidia.com/rtx/dlss) · NVIDIA · DLSS Frame Generation рассчитана на работу вместе с 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,4 GHz - [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 и соседские роутеры ухудшают качество радиосигнала → Передача на беспроводном участке не удаётся → несколько повторных передач → Пакеты приходят неравномерно (джиттер), персонажи двигаются рывками, а в тяжёлых случаях из-за потерь телепортируются - Симптомы: Микрофризы, Телепортация, Откидывание назад / Факторы: Джиттер, Потери - У кого: Только у меня, Все в одном доме / Когда: Изредка, случайно, Всегда - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Автоматически подстраивать длину буфера интерполяции под состояние подключения, при большом джиттере или потерях показывать на экране индикатор состояния сети. - Внешние стороны, задачи: Посоветовать игрокам кабельное подключение, диапазоны 5 GHz или 6 GHz, перестановку роутера. - Цифры для ориентира: Каждая повторная передача добавляет примерно 1–4 ms. При слабом сигнале пакет много раз пересылается на низкой скорости и к тому же ждёт, пока освободится радиоканал, поэтому бывают всплески по 50–200 ms. Подвох в том, что средний пинг при этом выглядит нормальным. - На графике: Случайные всплески (RTT до роутера) - Где смотреть: Несколько минут пинговать адрес роутера (основной шлюз в выводе ipconfig) командой ping /t и посмотреть уровень сигнала и радиоканал своего роутера через netsh wlan show networks mode=bssid. Сравнить с кабельным подключением на том же месте - Подтверждает: Уже пинг до роутера беспорядочно скачет на десятки или сотни ms, иногда бывают потери, уровень сигнала низкий. По кабелю или рядом с роутером проблема пропадает - Опровергает: Если до роутера ровно, а скачет только дальше, дело в подключении или на участке провайдера. Если всплески идут только через равные промежутки, это «Фоновое сканирование Wi-Fi» - Чем проверить: Проверка на стороне игрока - Подробнее: Если в mesh-системе Wi-Fi узлы связаны друг с другом по радио (беспроводной бэкхол), ретранслирующий узел не может передавать, пока принимает, и делит эфирное время с соседними участками на том же радиоканале. Под нагрузкой пропускная способность падает, а задержка растёт. В устройствах с отдельным радиодиапазоном под бэкхол это выражено слабее, а если соединить узлы кабелем (Ethernet), этот участок перестаёт быть беспроводным. Адаптеры для передачи по электросети (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,4 GHz. Рекомендуется перейти на 5 GHz и выбрать радиоканал с меньшими помехами - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · CSMA/CA в 802.11: передача только при свободном радиоканале, а если он занят, передача откладывается до его освобождения и ещё на случайный интервал (backoff) - [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. У устройства, подключённого на низкой скорости (со слабым сигналом), медиана превышает 200 ms даже с FQ-CoDel - [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 узел не может передавать, пока принимает, а соседние участки мешают друг другу, поэтому пропускная способность цепочки ретрансляторов теоретически падает до 1/3 (в моделировании примерно до 1/7) - [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) использует CSMA/CA, как Wi-Fi, и из-за кратковременной неравномерности доступа джиттер может расти. Качество связи меняется из-за помех от бытовой техники и её включения и выключения (в масштабе от минут до часов) #### hn-channel · Перегруженный радиоканал Wi-Fi · Crowded Wi-Fi channel Там, где рядом десятки роутеров (например, в многоквартирном доме), они делят один радиоканал, и каждому приходится ждать своей очереди на передачу. - Почему → Следствие → На экране: Десятки роутеров работают на одном радиоканале 2,4 GHz → Перед передачей нужно ждать, пока другие устройства закончат и радиоканал освободится → Вечером, когда люди возвращаются домой, растёт джиттер (неравномерность интервалов между пакетами), появляются микрофризы - Симптомы: Микрофризы, Задержка ввода / Факторы: Джиттер, Задержка - У кого: Все в одном доме / Когда: Вечерний пик - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: При росте джиттера автоматически удлинять буфер интерполяции. - Внешние стороны, задачи: Посоветовать игрокам диапазоны 5 GHz или 6 GHz, менее загруженный радиоканал, кабельное подключение. - На графике: Высоко только в определённые часы (RTT и джиттер до роутера) - Где смотреть: Посмотреть радиоканалы и уровень сигнала соседних сетей Wi-Fi через netsh wlan show networks mode=bssid и сравнить пинг до роутера вечером и днём - Подтверждает: На том же радиоканале 2,4 GHz видно много соседних роутеров, а джиттер до роутера растёт только вечером. При переходе на 5 GHz, 6 GHz или менее загруженный радиоканал он уменьшается - Опровергает: Если скачет в любое время суток, это «Помехи и слабый сигнал Wi-Fi». Если до роутера всё в порядке, а вечером плохо только дальше, это «Перегрузка пиринга в часы пик» - Чем проверить: Проверка на стороне игрока - Источники: - [Recommended settings for Wi-Fi routers and access points](https://support.apple.com/en-us/102766) · Apple · Источник помех: другие роутеры и устройства на том же радиоканале. Для 2,4 GHz рекомендуется ширина 20 MHz, в 5 GHz и 6 GHz помех меньше - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · В 802.11 при занятом радиоканале передача откладывается до его освобождения и выполняется после случайной задержки backoff (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 (очередь в роутере) · Bufferbloat Когда кто-то из домашних выкладывает видео или качает большой файл, в очереди роутера скапливаются пакеты на сотни ms, и игровые пакеты ждут за ними. - Почему → Следствие → На экране: Подключение забито: домашние выгружают видео или делают облачный бэкап, игрок сам ведёт стрим, идёт большая загрузка → Роутер или модем складывает лишние пакеты в длинную очередь → Игровые пакеты тоже ждут в конце очереди, и пинг взлетает до сотен ms - Симптомы: Задержка ввода, Перемотка, Телепортация / Факторы: Задержка, Джиттер - У кого: Все в одном доме, Только у меня / Когда: Изредка, случайно, Вечерний пик - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Если пинг вдруг вырастает до сотен ms, показывать на экране состояние сети (с подсказкой, что подключение может быть занято большой передачей данных). - Внешние стороны, задачи: Посоветовать игрокам роутер с SQM (fq_codel, CAKE) или QoS, скорость в SQM выставлять на 90–95% от скорости подключения (только тогда очередь возникает внутри роутера и SQM работает), ограничить скорость отдачи. - Цифры для ориентира: На подключении с отдачей 10 Mbps буфер в 1 MB даёт очередь длиной до 800 ms. - На графике: Растёт вслед за онлайном и нагрузкой (RTT, загрузка подключения на отдачу и приём) - Где смотреть: Держа пинг запущенным, полностью загрузить подключение тестом скорости или воспользоваться веб-тестом, который измеряет задержку под нагрузкой (по рекомендациям Bufferbloat.net). Смотреть вместе с загрузкой на отдачу и приём в интерфейсе роутера - Подтверждает: Пока отдача или приём забивают подключение, пинг растёт до сотен ms, а после окончания передачи возвращается (подозрительно, если задержка под нагрузкой больше 50 ms). С включённым SQM проблема пропадает - Опровергает: Если подключение свободно, а пинг всё равно скачет, это «Помехи и слабый сигнал 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. Если исправить беспроводные очереди, задержка под нагрузкой падает примерно до 1/10 от прежней - [Tests for Bufferbloat](https://www.bufferbloat.net/projects/bloat/wiki/Tests_for_Bufferbloat/) · Bufferbloat.net · Если держать пинг запущенным, загрузить подключение тестом скорости и пинг вырастет, это bufferbloat. При задержке под нагрузкой больше 50 ms (или оценке ниже B) рекомендуется принимать меры #### hn-nat · Истечение записи в таблице NAT · NAT mapping timeout Роутер удаляет из таблицы NAT неактивные соединения, по которым какое-то время не было пакетов. Это частая причина дисконнекта в тот момент, когда игрок после простоя снова начинает двигаться. - Почему → Следствие → На экране: Роутер записывает соединение «домашнее устройство ↔ внешний сервер» в таблицу NAT (таблицу трансляции адресов) → Если пакетов какое-то время нет, запись удаляется (для UDP обычно через 30–120 секунд) → Пакеты сервера больше не попадают в домашнюю сеть, дисконнект - Симптомы: Дисконнект / Факторы: Потери - У кого: Только у меня, Все в одном доме / Когда: После бездействия - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: отправлять хартбиты с интервалом не больше половины самого короткого таймаута простоя (запись NAT для UDP гарантированно обновляют только исходящие из дома пакеты, поэтому отправляет клиент), при обрыве автоматически переподключаться. Сервер: отвечать на хартбиты, а если их долго нет, первым закрывать соединение. Если запись NAT удалилась и внешний адрес и порт сменились, узнавать того же игрока по токену сессии (идентификатору, полученному при входе) и продолжать сессию. - На графике: Массовый обрыв соединений (число обрывов (таймаут хартбита), время простоя перед обрывом) - Где смотреть: Собрать причины обрывов на сервере и время от последнего пакета по соединению до обрыва (время простоя) и посмотреть их распределение. В тесте увеличивать интервал между 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 · Таймер записи NAT для 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, разумный интервал keep-alive около 30 секунд, более частые keep-alive зря тратят трафик и энергию #### hn-router · Слабый или перегретый роутер · Router CPU / session table exhaustion Когда на дешёвый роутер приходятся десятки устройств и тысячи соединений, он сам перестаёт справляться. - Почему → Следствие → На экране: Десятки устройств, P2P-программы и торренты открывают тысячи соединений → CPU и таблица сессий роутера забиты → Задержки и потери при обработке пакетов, новые соединения не устанавливаются - Симптомы: Микрофризы, Ошибка входа / бесконечная загрузка, Дисконнект / Факторы: Потери, Джиттер - У кого: Все в одном доме / Когда: Чем дольше без перезапуска, Изредка, случайно - Основной ответственный: Внешние стороны · Внешние стороны - Внешние стороны, задачи: Посоветовать игрокам перезагрузить роутер (временная мера), заменить его, закрыть программы, которые открывают много соединений (P2P, торренты). - На графике: Упор в лимит (плато) (CPU и число соединений роутера, RTT до роутера) - Где смотреть: Посмотреть в веб-интерфейсе роутера загрузку CPU, число соединений (сессий) и подключённых устройств (если роутер это показывает) и сравнить пинг до самого роутера до и после перезагрузки - Подтверждает: При большом числе соединений уже пинг до роутера скачет или теряются пакеты, новые подключения не проходят. После перезагрузки какое-то время всё нормально, потом снова плохо - Опровергает: Если до роутера всё в порядке, а плохо только дальше, дело в подключении или на участке провайдера - Чем проверить: Проверка на стороне игрока - Источники: - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · Число TCP-соединений к одному порту сервера, которое пропускает домашний роутер, от 16 до примерно 1 024 (медиана 135). Пропускная способность дешёвых устройств бывает всего несколько Mbps - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Максимальное число записей в таблице conntrack в 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,5 ms на той же частоте, 40–60 ms между разными частотами - [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 в среднем 30 ms, между сотами 5G (NSA) в среднем 108 ms #### hn-rrc · Задержка перехода состояний RRC (энергосбережение радиомодуля) · Radio state promotion (RRC) Если связи какое-то время нет, телефон переводит радиосоединение в экономичное состояние, а при следующем пакете поднимает его снова, и пакет опаздывает. - Почему → Следствие → На экране: После короткой паузы в обмене данными телефон переводит радиосоединение в режим энергосбережения → Чтобы отправить следующий пакет, соединение нужно снова поднять → Заметно запаздывает только первое действие после простоя - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Только у меня / Когда: После бездействия - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Поддерживать соединение активным лёгкой периодической отправкой (ценой расхода батареи). - Цифры для ориентира: В LTE радиомодуль обычно уходит в энергосбережение примерно после 10 секунд без обмена данными, а возврат занимает от десятков до сотен ms (по измерениям примерно 0,3–0,6 с). В 3G больше 1 секунды. - На графике: Высоко только у некоторых (RTT первого запроса после простоя (мобильные)) - Где смотреть: Разбить внутриигровой RTT по интервалу с предыдущим обменом данными. На мобильных сравнить RTT первого пакета после паузы дольше 10 секунд и пакетов, отправленных подряд - Подтверждает: В мобильной сети только первый пакет после паузы опаздывает на сотни 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 секунд, медианная задержка выхода из энергосбережения 435 ms (25–75%: 319–558 ms), в 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 · Требование к задержке плоскости управления при переходе из ожидания в активное состояние: меньше 100 ms (без пейджинга и проводного участка) #### 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, и при каждом переключении пинг скачет или связь ненадолго прерывается. - Почему → Следствие → На экране: Игрок там, где сигнал 5G то есть, то нет (внутри здания, на границе покрытия 5G) → Телефон постоянно переключается между 5G и LTE, и каждый раз возникает короткий перерыв → Даже на месте пинг беспорядочно скачет, иногда бывают фризы и телепортация - Симптомы: Микрофризы, Телепортация, Фриз / Факторы: Джиттер, Потери - У кого: Только у меня / Когда: Изредка, случайно, В движении и при смене локации - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: При росте джиттера автоматически удлинять буфер интерполяции, писать в лог, снятый в момент лагов, смену типа сети (5G, LTE), чтобы различать причины. - Внешние стороны, задачи: Посоветовать игрокам для сравнения включить в настройках предпочтительный режим LTE, рекомендовать Wi-Fi. - Цифры для ориентира: Одно переключение занимает от десятков до сотен ms. 5G в Корее в основном работает в связке с LTE (NSA), поэтому 5G-часть легко то подключается, то отваливается. - На графике: Случайные всплески (RTT, смена типа сети (5G, LTE)) - Где смотреть: Переключить телефон в режим предпочтения LTE и сравнить на том же месте. Надёжнее, если клиент записывает вместе с RTT изменения индикатора сети в TelephonyDisplayInfo на Android (OVERRIDE_NETWORK_TYPE_NR_NSA и др.) - Подтверждает: Моменты всплесков 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: в среднем 108 ms (4G → 5G 80 ms). Сразу после переключений с участием 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 и может установить или уже установило двойное подключение (EN-DC) к 5G (NR) #### 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 и DPI на уровне страны или провайдера» - Чем проверить: Проверка на стороне игрока - Источники: - [RFC 8952: Captive Portal Architecture](https://www.rfc-editor.org/rfc/rfc8952) · IETF · Captive portal: сеть, в которой доступ ограничен, пока не выполнены условия вроде согласия с правилами или авторизации - [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 Даже свет в оптоволокне проходит всего около 200 000 km в секунду. Если сервер далеко, задержка будет большой, каким бы хорошим он ни был. - Почему → Следствие → На экране: Сервер далеко (зарубежный сервер, другой континент) → Время пути туда и обратно растёт с расстоянием (не меньше 10 ms на каждые 1 000 km) → Постоянная задержка ввода на каждое действие, невыгодное положение при проверке попаданий - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Один регион или провайдер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда инфраструктуры · Сетевая инфраструктура, Команда разработки · Разработка сервера - Команда разработки, задачи: Законы физики кодом не исправить, можно только смягчить последствия: дать игроку выбрать ближайший региональный сервер, компенсацией задержки (отмоткой времени назад) уменьшить невыгодное положение при проверке попаданий. - Команда инфраструктуры, задачи: Серверы и ОС: ставить региональные серверы там, где много игроков. Сеть: размещать точки подключения (edge) ближе к игрокам, выбирать линии и маршруты с минимальными обходами. - Цифры для ориентира: Сеул–Токио около 30 ms, Сеул–Сингапур около 75 ms, Сеул–запад США около 140 ms, Сеул–Европа около 230–270 ms (туда и обратно, по реальным маршрутам). Вдоль прямой до Европы почти нет крупных кабелей, и трафик идёт через Юго-Восточную Азию и Суэц или через США, поэтому задержка намного больше, чем следует из расстояния. - На графике: Высоко с самого начала (RTT (по странам и регионам)) - Где смотреть: Определить страну по IP подключения и посмотреть распределение RTT по странам, измерить ping и traceroute до сервера с VM в ближайшем к этой стране облачном регионе или с зондов RIPE Atlas (их выбирают по стране и ASN) - Подтверждает: RTT для далёких стран высокий в любое время суток и близок к минимальной задержке по расстоянию (10 ms туда и обратно на 1 000 km) и к публичной статистике задержек - Опровергает: Если RTT намного выше, чем объясняет расстояние, это «Неоптимальная маршрутизация», если растёт только по вечерам, это «Перегрузка пиринга в часы пик» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Реальные инциденты: 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 (около 200 000 km в секунду, 10 ms туда и обратно на 1 000 km) - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Медианы измеренного времени туда и обратно от Сеула (Korea Central): Токио 30 ms, Сингапур 68 ms, запад США 124–136 ms, Европа 234–244 ms - [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 000 km, поэтому сам путь сигнала длинный. На низкой орбите маршрут терминал–спутник–наземная станция часто перераспределяется, и в эти моменты ненадолго появляются задержка и потери → Через геостационарный спутник большая задержка ввода на любое действие, через низкоорбитальный обычно всё нормально, но через равные промежутки времени микрофризы и телепортация - Симптомы: Задержка ввода, Микрофризы, Телепортация / Факторы: Задержка, Джиттер, Потери - У кого: Только у меня, Все в одном доме, Один регион или провайдер / Когда: Всегда, С постоянным периодом - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента, Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: автоматически удлинять буфер интерполяции под джиттер, дублировать отправку ввода, чтобы переживать короткие потери, показывать качество соединения. Сервер: учитывать задержку спутниковых подключений при выборе окон проверки и предела компенсации задержки, ставить таймаут, который не выкидывает игрока из-за паузы около 1 с. - Внешние стороны, задачи: Предупредить игроков, что у спутникового интернета задержка бывает большой или периодически скачет, и посоветовать для соревновательного контента по возможности использовать наземное проводное подключение. - Цифры для ориентира: Для геостационарного спутника (высота 36 000 km) один только путь сигнала через космос занимает 260 ms в одну сторону, так что туда и обратно выходит больше 520 ms (ITU-T G.114). У низкоорбитального Starlink, по официальным данным (средние значения за 15 с), медиана в часы пик в США 33 ms, и даже худший 1% (p99) ниже 65 ms (2024 год). По замерам исследователей задержка менялась в моменты перераспределения маршрута каждые 15 с, и возникали обрывы короче 1 с. В замерах бортового интернета 2018 года средняя задержка туда и обратно при спутниковом подключении составила 750 ms. - На графике: Высоко только у некоторых (RTT и джиттер (по ASN операторов спутникового интернета)) - Где смотреть: Проверить, принадлежит ли ASN адреса подключения оператору спутникового интернета, и отдельно построить распределение и временной ряд RTT для его абонентов. Несколько минут подряд измерять ping до сервера с зондов RIPE Atlas в этой ASN или попросить игрока держать ping включённым и замерить интервал между скачками - Подтверждает: У геостационарных операторов RTT всегда выше 500 ms, у низкоорбитальных обычно десятки ms, но примерно каждые 15 с RTT меняется или связь ненадолго обрывается - Опровергает: Если оператор не спутниковый, а RTT всегда высокий, это «Задержка распространения (физическое расстояние)» или «Неоптимальная маршрутизация», если скачет нерегулярно, дело в сигнале Wi-Fi или мобильной сети - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: До низкоорбитального спутника недалеко (один участок у Starlink 1,8–3,6 ms), поэтому обычная задержка бывает близка к задержке наземных линий. Зато если точка выхода в интернет от наземной станции (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 · Значения для планирования: задержка распространения на спутниковом участке в одну сторону при высоте 400 km 12 ms, 14 000 km 110 ms, 36 000 km (геостационарная орбита) 260 ms - [Improving Starlink’s Latency](https://starlink.com/public-files/StarlinkLatency.pdf) · Starlink · Медиана в часы пик в США 48,5 ms → 33 ms, самый медленный 1% (p99) больше 150 ms → меньше 65 ms (2024 год), распространение на одном спутниковом участке 1,8–3,6 ms, обход по лазерным линиям связи добавляет задержку, расстояние от наземной станции до точки выхода в интернет (PoP) тоже влияет на задержку - [A Multifaceted Look at Starlink Performance (WWW 2024)](https://www.nitindermohan.com/documents/2024/pubs/starlinkWWW2024.pdf) · ACM · Starlink каждые 15 с одновременно по всему миру перераспределяет маршруты, на этих границах колеблются задержка и пропускная способность и бывают обрывы короче 1 с (переключение между спутниками тут ни при чём), задержка на участке терминал ↔ спутник ↔ наземная станция около 40 ms - [Mile High WiFi: A First Look at In-Flight Internet Connectivity (WWW 2018)](https://aqualab.cs.northwestern.edu/publication/2018/jrula-www18/) · ACM · 45 часов замеров бортового интернета: средняя задержка туда и обратно 200 ms через наземные базовые станции и 750 ms через спутник, медиана потерь при спутниковом подключении 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 Из-за договоров о соединении между провайдерами трафик даже до близкого сервера идёт в обход через далёкие точки. - Почему → Следствие → На экране: Провайдер игрока и провайдер сервера не соединены напрямую → Трафик идёт через другую страну или другой город, растут расстояние и число устройств на пути → Пинг заметно выше только у абонентов определённого провайдера - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Один регион или провайдер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Внешние стороны · Внешние стороны - Команда инфраструктуры, задачи: Подключиться к нескольким провайдерам (мультихоминг), отслеживать пинг по провайдерам и находить тех, чей трафик идёт в обход, договариваться с провайдерами о корректировке маршрутов. - Внешние стороны, задачи: Попросить провайдера скорректировать маршрут. - Цифры для ориентира: Даже внутри одной страны пинг в зависимости от маршрута может отличаться в два-три раза. - На графике: Высоко с самого начала (RTT (по провайдерам и ASN)) - Где смотреть: Сравнить RTT по провайдерам (ASN). По traceroute и mtr с зондов RIPE Atlas медленного провайдера или от игроков посмотреть, через какие страны и города идёт маршрут. IPv4 и IPv6 измерять отдельно (mtr -4, -6) - Подтверждает: В одном и том же регионе RTT всегда высокий только у определённого провайдера, и маршрут проходит через другую страну или далёкий город. Либо высокий только у одного семейства адресов (IPv4 или IPv6) - Опровергает: Если у всех провайдеров одинаково высокий, это «Задержка распространения (физическое расстояние)», если высокий только по вечерам, это «Перегрузка пиринга в часы пик» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Маршруты для IPv4 и IPv6 выбираются независимо, поэтому даже до одного и того же сервера один из протоколов может идти далёким обходом и работать медленнее (замеры APNIC 2016 года: внутри одного провайдера нашлись отдельные группы пользователей, у которых IPv6 медленнее IPv4 на 15 ms, 25 ms и 75 ms). Приложения с Happy Eyeballs (RFC 8305), которые из IPv6 и IPv4 выбирают тот, что подключится первым, сначала пробуют IPv6, и если IPv6 подключается в пределах рекомендованных 250 ms, IPv4 уже не пробуют. Поэтому соединение легко устанавливается по IPv6, даже если этот путь немного медленнее. Если пинг высокий только у одного провайдера, 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 раза длиннее прямого оптоволоконного пути, бывают случаи, когда пакет между двумя близкими точками идёт через другую сторону земного шара (hairpinning) - [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 пробуют первым, а до следующей попытки подключения рекомендуется ждать 250 ms - [IPv6 Performance – Revisited](https://blog.apnic.net/2016/08/22/ipv6-performance-revisited/) · APNIC · Сравнение времени туда и обратно по IPv6 и IPv4 у одних и тех же пользователей с двойным стеком: сети доступа иногда обрабатывают пакеты IPv6 совсем иначе, и внутри одного провайдера появляются группы, где IPv6 медленнее на 15 ms, 25 ms и 75 ms #### isp-peak · Перегрузка пиринга в часы пик · Peak-hour congestion at peering Примерно с 21:00 до 23:00 резко растёт видеотрафик, и стыки между провайдерами (пиринг) легко перегружаются. - Почему → Следствие → На экране: По вечерам массово смотрят стримы и скачивают файлы → На пиринговых стыках появляются очереди и потери → Только по вечерам у абонентов определённого провайдера микрофризы и телепортация - Симптомы: Микрофризы, Телепортация, Откидывание назад / Факторы: Джиттер, Потери, Задержка - У кого: Один регион или провайдер / Когда: Вечерний пик - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Внешние стороны · Внешние стороны - Команда инфраструктуры, задачи: Расширять прямые подключения к этому провайдеру, обходить перегруженные маршруты, отслеживать потери и пинг по провайдерам в вечерние часы. - Внешние стороны, задачи: Попросить провайдера расширить пиринговые стыки. - На графике: Высоко только в определённые часы (RTT и потери (по провайдерам)) - Где смотреть: Построить RTT и потери по провайдерам (ASN) по часам, снять mtr вечером и днём с зондов RIPE Atlas этого провайдера или у игроков и найти участок, с которого начинаются потери - Подтверждает: Только у определённого провайдера каждый вечер примерно с 21:00 до 23:00 растут 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 После обрыва подводного кабеля трафик несколько недель (иногда месяцев), пока идёт ремонт, ходит дальними обходными маршрутами, а оставшиеся линии перегружены. - Почему → Следствие → На экране: Обрыв кабеля или отказ оборудования → Трафик уходит на дальние обходные маршруты и оставшиеся линии → У зарубежных игроков резко растёт пинг и появляются потери, это длится от нескольких дней до нескольких недель - Симптомы: Задержка ввода, Телепортация / Факторы: Задержка, Потери - У кого: Один регион или провайдер / Когда: Всегда - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда инфраструктуры · Сетевая инфраструктура - Команда инфраструктуры, задачи: Иметь линии по другим маршрутам и при аварии переводить трафик на них. - Внешние стороны, задачи: Сообщить зарубежным игрокам причину и ожидаемый срок восстановления, запросить у оператора линии график ремонта. - На графике: Ступенька вверх с определённого момента (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 года, в июле всё ещё ремонтировались (зона конфликта), обрыв EASSy и Seacom в мае устранили за 19 дней - [Q1 2024 Internet disruption summary](https://blog.cloudflare.com/q1-2024-internet-disruption-summary/) · Cloudflare · Обрыв кабелей у Западной Африки (14 марта) устранили через 3–6 недель, всё это время трафик переводили на другие кабели #### isp-bgp · Смена маршрута BGP и сходимость · Route change / BGP convergence Когда в интернете меняется маршрутная информация, пакеты теряются, пока маршруты снова не сойдутся: от нескольких секунд до нескольких десятков секунд (изредка несколько минут). - Почему → Следствие → На экране: Меняется маршрутная информация на участке какого-то провайдера → От нескольких секунд до нескольких десятков секунд пакеты пропадают или переходят на новый маршрут → Внезапный фриз на несколько секунд, после которого меняется пинг (например, 40 → 70 ms) - Симптомы: Фриз, Телепортация / Факторы: Потери, Задержка - У кого: Один регион или провайдер / Когда: Изредка, случайно - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера, Внешние стороны · Внешние стороны - Команда разработки, задачи: Таймауты, которые переживают короткие обрывы (не разрывать сразу соединение, замершее на несколько секунд). - Команда инфраструктуры, задачи: Мониторить маршруты (следить за изменениями маршрутов и пинга для наших диапазонов IP), отказы наших линий обнаруживать через BFD меньше чем за 1 с и переключаться (стандартный hold time в BGP 90–180 с), если маршрут сменился на дальний и не возвращается, переводить трафик на другую линию. - Внешние стороны, задачи: Если маршрут часто меняется на участке какого-то провайдера, попросить его выяснить причину. - На графике: Ступенька вверх с определённого момента (RTT, маршрут traceroute) - Где смотреть: Сравнить маршруты traceroute и mtr до и после момента, когда изменился RTT, и посмотреть в RIPEstat BGPlay историю изменений маршрутов BGP для нашего диапазона адресов (префикса) - Подтверждает: Вместе с фризом на несколько секунд RTT переходит на другое значение, и в тот же момент есть обновления BGP и смена AS-пути - Опровергает: Если изменений маршрута не было, а RTT растёт только по вечерам, это «Перегрузка пиринга в часы пик», если плохо только части соединений, это «Неисправность одного из путей ECMP» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Реальные инциденты: cloudflare-2020, meta-2021, cloudflare-dns-2025 - Источники: - [RFC 4271: A Border Gateway Protocol 4 (BGP-4)](https://www.rfc-editor.org/rfc/rfc4271) · IETF · Рекомендуемое значение hold time в BGP по умолчанию 90 с (если за это время от соседа нет сообщений, сессия разрывается) - [BGP updates in 2024](https://blog.apnic.net/2025/01/07/bgp-updates-in-2024/) · APNIC · Среднесуточное время, за которое нестабильный маршрут снова стабилизируется: 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 · Показывает маршруты BGP для диапазона адресов (префикса) на начало периода, обновления BGP, замеченные за этот период, и сведения об AS на пути #### isp-ecmp · Неисправность одного из путей ECMP · ECMP / link bundle member fault Провайдеры и ЦОД держат несколько путей к одной цели и для каждого соединения выбирают один из них. Если неисправен только один путь, лаги постоянно бывают только у тех, кому достался этот путь. - Почему → Следствие → На экране: На участке из нескольких объединённых линий одна линия или одно устройство неисправно или перегружено → Путь выбирается по сочетанию адресов и портов (хешу), поэтому потери и задержка только у соединений, попавших на этот путь → В одном регионе и у одного провайдера телепортация постоянно бывает только у части игроков. После переподключения иногда всё проходит - Симптомы: Телепортация, Откидывание назад, Микрофризы / Факторы: Потери, Джиттер - У кого: Только у меня, Один регион или провайдер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера, Внешние стороны · Внешние стороны - Команда разработки, задачи: Вести статистику потерь и повторных передач по соединениям, чтобы можно было выгрузить IP, порты и время у пострадавших игроков (для TCP число повторных передач из TCP_INFO, для UDP расчёт по пропущенным номерам пакетов). - Команда инфраструктуры, задачи: Собрать IP, порты и время у пострадавших игроков и передать провайдеру или ЦОД, отслеживать потери по путям, измерять маршрут тем же протоколом и портом, что и игра (mtr --tcp или --udp с --port), если путь проходит через наше оборудование, вывести неисправную линию или устройство из группы. - Внешние стороны, задачи: Попросить провайдера проверить и заменить неисправный путь, посоветовать игрокам временно обходить проблему переподключением (если при переподключении меняется порт). - Цифры для ориентира: Если путей 4, проблема бывает примерно у четверти пользователей. Замер пинга может пойти по другому пути, чем игровой трафик, и показать норму. - На графике: Высоко только у некоторых (потери и повторные передачи по соединениям (по IP и портам)) - Где смотреть: Разбить потери и повторные передачи по соединениям по IP и порту источника. Запускать mtr по UDP (-u) на игровой порт (-P) с фиксированным портом источника (-L) и повторить несколько раз с разными портами источника. Если задать только -P без -L, порт источника меняется с каждым запросом, и пути смешиваются - Подтверждает: В пределах одного региона и провайдера потери стабильно бывают только у определённых сочетаний портов (или адресов) источника, а после переподключения со сменой порта всё нормально - Опровергает: Если плохо при любом порте, это перегрузка или авария на всём участке - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Чтобы пакеты одного соединения не перемешивались, оборудование (ECMP, LAG) закрепляет за каждым соединением путь по значению, вычисленному из адресов и портов, а при некоторых настройках только из адресов. Там, где учитываются только адреса, переподключение не помогает: путь остаётся тем же. Поэтому если одновременно приходят жалобы «пинг нормальный, а игра лагает» и «после перезахода стало лучше», стоит заподозрить эту причину. - Источники: - [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 по хешу полей заголовка выбирают для каждого потока один линк и так сохраняют порядок пакетов (много потоков → один линк) - [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 Если превышен лимит трафика или тариф предусматривает управление определённым трафиком, пакеты задерживаются или отбрасываются. - Почему → Следствие → На экране: После исчерпания пакета трафика по тарифу скорость ограничена, либо ограничен определённый трафик → Пакеты ждут в очереди или отбрасываются → Лаги после определённого объёма трафика, особенно в мобильной сети - Симптомы: Задержка ввода, Телепортация / Факторы: Задержка, Потери - У кого: Только у меня, Один регион или провайдер / Когда: Всегда, Вечерний пик - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка сервера, Команда инфраструктуры · Сетевая инфраструктура - Команда разработки, задачи: Сократить игровой трафик (сжатие, отправка только нужного). - Команда инфраструктуры, задачи: Если игровой трафик задерживается или отбрасывается только у определённого провайдера, собрать данные и эскалировать провайдеру. - Внешние стороны, задачи: Попросить игроков проверить, не исчерпан ли трафик по тарифу и не ограничена ли скорость, а также не расходуют ли трафик другие приложения на том же телефоне, запросить у провайдера, не ограничивает ли он игровой трафик. - Цифры для ориентира: На корейских мобильных тарифах после исчерпания трафика скорость обычно ограничивают до 1–5 Mbps, на дешёвых тарифах до сотен kbps. Самой игре трафика нужно немного, но если на том же телефоне его тратят другие приложения, перед оборудованием, ограничивающим скорость, выстраивается очередь. - На графике: Упор в лимит (плато) (пропускная способность, RTT) - Где смотреть: Попросить игрока проверить в приложении провайдера остаток трафика и наличие ограничения скорости и замерить максимальную скорость спидтестом. На стороне сервера сравнить потери и RTT по провайдерам - Подтверждает: Скорость упирается в одно значение, например 1–5 Mbps или сотни kbps, и с этого момента RTT и потери растут, когда трафик тратят другие приложения на том же телефоне. После докупки трафика или перехода на Wi-Fi проблема пропадает - Опровергает: Если ограничения скорости нет, а плохо только у определённого провайдера, это «Перегрузка пиринга в часы пик» или «Неоптимальная маршрутизация» - Чем проверить: Проверка на стороне игрока - Источники: - [SKT, 고객 선택권과 혜택 강화한 신규 5G 요금제 출시](https://news.sktelecom.com/180213) · SK텔레콤 · Примеры ограничения скорости на тарифах 5G после исчерпания базового трафика: до 400 kbps, 1 Mbps, 3 Mbps - [SKT, 요금제 개편](https://news.sktelecom.com/225487) · SK텔레콤 · После исчерпания включённого трафика интернет продолжает работать на скорости до 400 kbps (услуга «Спокойный интернет для всех») #### isp-udp-block · Ограничение UDP и DPI на уровне страны или провайдера · UDP blocking, throttling and inspection by networks Некоторые сети блокируют отдельные UDP-адреса и порты или ограничивают скорость UDP, а оборудование инспекции пакетов (DPI) отсеивает протоколы, которые не может распознать. Игры, работающие по UDP, в таких сетях не подключаются или часто теряют соединение. - Почему → Следствие → На экране: Подключение из сети провайдера, который ограничивает скорость UDP, или из сети с оборудованием проверки трафика (цензуры) на уровне страны или провайдера → Блокируются отдельные UDP-адреса и порты, в часы нагрузки ограничивается скорость UDP, отсеиваются порты и протоколы не из белого списка, или пропускаются только первые несколько пакетов, а дальше включается блокировка → Только у игроков из определённой страны или у определённого провайдера ошибка входа или бесконечная загрузка, дисконнект вскоре после входа, телепортация из-за потерь в часы нагрузки - Симптомы: Ошибка входа / бесконечная загрузка, Дисконнект, Телепортация / Факторы: Потери - У кого: Один регион или провайдер / Когда: Сразу после входа или техработ, Всегда, Вечерний пик - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента, Команда разработки · Разработка сервера, Команда инфраструктуры · Сетевая инфраструктура - Команда разработки, задачи: Клиент: если по UDP не удаётся подключиться за несколько секунд, автоматически переходить на запасной путь через TCP/TLS 443, распознавать и случаи, когда соединение сначала устанавливается, но вскоре рвётся, и повторять попытку по запасному пути, писать в лог, по какому пути прошло подключение. Сервер: принимать тот же игровой протокол и по TCP 443 (TLS), подстроить таймауты, потому что на запасном пути задержка может вырасти. - Команда инфраструктуры, задачи: Перед запуском в новой стране измерить в сетях местных провайдеров, доходит ли UDP и какие потери в часы пик, разместить ближе к этой стране relay-серверы или шлюзы, принимающие запасной путь через TCP 443, отслеживать долю успешных подключений по UDP и TCP по странам и ASN, если у провайдера подтверждено ограничение скорости UDP, собрать данные и эскалировать. - Внешние стороны, задачи: Запросить у провайдера или ведомства критерии ограничения UDP и возможность их смягчить, посоветовать игрокам подключиться из другой сети и сравнить. - Цифры для ориентира: По замерам, на которые ссылается документ IETF, 3–5% сетей полностью блокируют UDP. По данным Google о QUIC (работает поверх UDP) за 2016 год, 4,4% клиентов не могли им пользоваться: UDP или QUIC был заблокирован либо MTU пути был слишком мал. В основном это были клиенты за корпоративными файрволами, а блокировки на уровне целого провайдера не встречались. Ещё 0,3% клиентов находились в сетях, которые, судя по резкому росту потерь в часы пик, ограничивали скорость UDP. После обращений к провайдерам эту долю удалось снизить с 1% в 2015 году. - На графике: Высоко только у некоторых (доля успешных подключений по UDP (по странам и ASN)) - Где смотреть: Отдельно смотреть долю успешных подключений по UDP и по запасному пути TCP 443 в разрезе стран и ASN. С облачной VM в сети этого провайдера или с ПК игрока проверить подключение отдельно на игровой UDP-порт и на TCP 443 и сравнить через mtr -u -P (игровой порт) и mtr -T -P 443, с какого участка пропадают ответы - Подтверждает: Только из определённой страны или ASN по UDP нет первого ответа или соединение рвётся через несколько секунд, а TCP 443 оттуда же работает нормально. При ограничении скорости потери UDP заметно растут только в часы пик, а TCP страдает меньше - Опровергает: Если вместе с UDP не работает и TCP, смотреть аварии на маршруте, блокировку IP и «Сбои и задержки DNS». Если во всех странах одинаково, проверить настройки наших серверов и файрвола, если потери и у UDP, и у TCP только в моменты большого объёма передачи, это «Отбрасывание избытка полисером» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Оборудование DPI выбирает UDP-потоки по адресам, портам и протоколу и блокирует их, а иногда работает по белому списку и блокирует всё, кроме разрешённых протоколов (по обзору IRTF). Если оборудование судит лишь по некоторым полям пакета, блокировка может сработать даже из-за небольшого изменения протокола. На раннем этапе QUIC один файрвол после изменения одного бита в заголовке пропускал первые несколько пакетов, а следующие блокировал, и логика клиента, переключающаяся на 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 (рост потерь в часы пик, после обращений к провайдерам доля снизилась с 1% в 2015 году), случай с файрволом, который после изменения 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 Плохой контакт в разъёмах, старый кабель или неисправный модем дают постоянные потери и периодические обрывы связи. - Почему → Следствие → На экране: Повреждённый кабель, плохой контакт, неисправность модема или оптического терминала → Пакеты отбрасываются из-за битовых ошибок, иногда связь пропадает на время от нескольких секунд до минуты, пока линия переподключается → Постоянные небольшие потери, иногда фриз на несколько секунд или дисконнект - Симптомы: Телепортация, Фриз, Дисконнект / Факторы: Потери - У кого: Все в одном доме / Когда: Изредка, случайно - Основной ответственный: Внешние стороны · Внешние стороны - Внешние стороны, задачи: Попросить игрока проверить, обрываются ли другие игры и видеозвонки, и если да, посоветовать вызвать провайдера для проверки линии. - На графике: Случайные всплески (доля потерь, журнал переподключений линии) - Где смотреть: Несколько минут измерять 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 · Некоторое время пингует каждый участок, считает долю потерь по маршрутизаторам и линкам и показывает, на каком участке возникают потери #### isp-dns · Сбои и задержки DNS · DNS failure / slowness DNS превращает имя сервера в адрес. Если DNS отвечает медленно или с ошибкой, клиент не может найти сервер авторизации и сервер патчей. - Почему → Следствие → На экране: Сбой 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 Массированная атака на игровую компанию или на другого клиента в той же сети забивает общие линии связи. - Почему → Следствие → На экране: Появляется огромный объём атакующего трафика → Задерживается и отбрасывается даже нормальный трафик на тех же линиях → У многих игроков одновременно телепортация, дисконнекты, ошибки входа - Симптомы: Телепортация, Дисконнект, Ошибка входа / бесконечная загрузка / Факторы: Потери, Задержка - У кого: Весь сервер, Один регион или провайдер / Когда: Изредка, случайно, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Внешние стороны · Внешние стороны - Команда инфраструктуры, задачи: Подключить сервис защиты от 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 · Ставить перед origin-серверами edge-сервисы вроде CloudFront и балансировщиков нагрузки, чтобы серверы не были видны напрямую - [RFC 7999: BLACKHOLE Community](https://www.rfc-editor.org/rfc/rfc7999) · IETF · BGP-сообщество BLACKHOLE, которым соседнему провайдеру сообщают, что трафик на определённый адрес нужно отбрасывать #### isp-cgnat · Общий IP провайдера (CGNAT) · Carrier-grade NAT В мобильных сетях и у некоторых провайдеров один IP делят много абонентов, а записи NAT для неактивных соединений быстро удаляются. - Почему → Следствие → На экране: Оборудование провайдера ведёт таблицу сессий для огромного числа абонентов → Лимит таблицы сессий, короткий таймаут простоя → Дисконнект после бездействия, ложные срабатывания, при которых блокируются сразу все игроки с одним IP - Симптомы: Дисконнект, Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Один регион или провайдер / Когда: После бездействия - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера, Команда инфраструктуры · Сетевая инфраструктура - Команда разработки, задачи: Клиент: слать хартбиты с интервалом не больше половины самого короткого таймаута простоя (в мобильных сетях он бывает около 30 с), причём слать должен именно клиент, потому что запись CGNAT у провайдера надёжно обновляется только исходящими изнутри пакетами, при обрыве автоматически переподключаться. Сервер: отвечать на хартбиты и первым закрывать соединение, если их нет определённое время, подхватывать игрока по токену сессии, даже если после смены записи NAT изменились адрес и порт, осторожно применять блокировки по IP, потому что один IP могут делить много людей (решать вместе с признаками аккаунта и устройства). - Команда инфраструктуры, задачи: Подстроить лимиты файрвола и защиты от DDoS на число соединений с одного IP и число новых соединений в секунду под общие IP провайдеров (для диапазонов мобильных операторов поднять пороги или сделать исключение). - Цифры для ориентира: В мобильных сетях таймаут простоя для UDP бывает около 30 с. - На графике: Массовый обрыв соединений (число обрывов (таймаут хартбита), время бездействия перед обрывом (по провайдерам)) - Где смотреть: Посмотреть в логах подключений, сколько аккаунтов одновременно подключено с одного IP и какой это провайдер (ASN), собрать по провайдерам время бездействия у соединений, оборвавшихся после простоя. На стороне игрока проверить адрес интернет-подключения (WAN) в интерфейсе роутера - Подтверждает: В диапазонах мобильных операторов с одного 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 · Измеренное время жизни UDP-записей NAT 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 (максимальный размер пакета за одну передачу) → Рост пинга и потери, ошибка входа из-за блокировки вместе с другими пользователями того же промежуточного адреса - Симптомы: Задержка ввода, Телепортация, Ошибка входа / бесконечная загрузка / Факторы: Задержка, Потери - У кого: Только у меня / Когда: Всегда, Сразу после входа или техработ - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда инфраструктуры · Сетевая инфраструктура, Команда разработки · Разработка сервера - Команда разработки, задачи: Держать UDP-пакеты не больше 1 200 байт (чтобы не было фрагментации, даже когда заголовки туннеля уменьшают MTU), решения о блокировке по IP принимать с учётом общих промежуточных адресов VPN и ускорителей, вместе с признаками аккаунта и устройства. - Команда инфраструктуры, задачи: Если много зарубежных игроков, ставить свои точки подключения ближе к ним, проверять маршруты провайдеров, от абонентов которых массово приходят жалобы вида «с ускорителем стало лучше». - Внешние стороны, задачи: Посоветовать игрокам выключить VPN или ускоритель и сравнить. - Цифры для ориентира: Близкий промежуточный сервер добавляет единицы ms, обход через другую страну добавляет от десятков до более чем 100 ms. - На графике: Высоко только у некоторых (RTT (по игрокам), владелец IP подключения) - Где смотреть: Проверить, принадлежит ли ASN адреса подключения VPN-сервису, ускорителю или хостинг-провайдеру, попросить игрока выключить VPN или ускоритель и сравнить пинг и traceroute - Подтверждает: RTT и потери растут или вход блокируется только с включённым VPN или ускорителем, а в traceroute виден участок через промежуточный сервер - Опровергает: Если с VPN или ускорителем и без них одинаково, дело в линии или участке провайдера. Если с ними лучше, проблема в обычном маршруте провайдера («Неоптимальная маршрутизация», «Перегрузка пиринга в часы пик») - Чем проверить: Проверка на стороне игрока - Подробнее: И наоборот, если маршрут провайдера плохой, ускоритель может пустить трафик лучшим путём, и пинг снизится. Жалобы «с ускорителем стало лучше» указывают на проблемы маршрутов провайдера, например неоптимальную маршрутизацию или вечернюю перегрузку. - Источники: - [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 · Сколько добавляет к времени туда и обратно узел в другой стране: Сеул–Токио 30 ms, Сеул–Гонконг 39 ms, Сеул–Сингапур 68 ms ### L5 Сетевое оборудование ЦОД (причин: 11) #### dc-firewall · Переполнение таблицы сессий файрвола · Firewall session table exhaustion Файрвол записывает каждое пропущенное соединение в таблицу сессий и отслеживает его. Когда таблица заполнена, новые соединения принять нельзя. - Почему → Следствие → На экране: Из-за наплыва подключений или атаки число сессий достигает лимита → Для нового соединения нет свободной записи, и оно отклоняется → У тех, кто пытается войти, ошибка входа или бесконечная загрузка, у части уже установленных соединений тоже дисконнект - Симптомы: Ошибка входа / бесконечная загрузка, Дисконнект / Факторы: Потери - У кого: Весь сервер / Когда: Сразу после входа или техработ, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера, Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: сдерживать наплыв подключений очередью на вход, переиспользовать соединения вместо повторяющихся коротких, первым закрывать соединения без хартбитов (чтобы мёртвые соединения не занимали таблицу сессий надолго). Клиент: слать хартбиты с интервалом не больше половины самого короткого таймаута простоя, при обрыве автоматически переподключаться, увеличивая интервал между попытками и добавляя случайный разброс (чтобы игроки не возвращались все разом). - Команда инфраструктуры, задачи: Увеличить таблицу сессий, быстрее удалять завершившиеся короткие соединения (сократить таймаут для закрытых сессий), при сокращении таймаута простоя сообщать новое значение команде разработки, чтобы подстроить интервал хартбитов, блокировать атаки, поставить алерт на заполнение таблицы сессий. - На графике: Упор в лимит (плато) (число сессий файрвола, число неудачных новых подключений) - Где смотреть: Вывести на график число одновременных сессий файрвола вместе с лимитом и найти в логах устройства записи об отброшенных пакетах, для которых не удалось создать сессию. На файрволе Linux сравнить nf_conntrack_count с nf_conntrack_max и поискать в dmesg «nf_conntrack: table full, dropping packet», на инстансе AWS смотреть conntrack_allowance_exceeded в ethtool -S - Подтверждает: С момента, когда число сессий упирается в лимит и выходит на плато, растёт число неудачных новых подключений, а вместе с ним записи об ошибках создания сессий или счётчик отброшенных пакетов - Опровергает: Если сессий намного меньше лимита, а войти нельзя, это «Переполнение очереди подключений (backlog)» или сервер авторизации. Если рвутся только неактивные соединения, это «Истечение отслеживания соединений в облачной группе безопасности» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Максимальное число записей в таблице conntrack (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 · Когда таблица conntrack заполнена, ядро пишет «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 Когда для отражения атаки трафик заворачивают через центр очистки, маршрут удлиняется, а нормальных пользователей защита иногда принимает за атаку и блокирует. - Почему → Следствие → На экране: После обнаружения атаки (или постоянно) входящий трафик идёт в обход через центр очистки → Маршрут удлиняется, часть нормальных пакетов признаётся атакой → Пинг растёт у всех, ошибка входа только в отдельных регионах или у отдельных провайдеров - Симптомы: Задержка ввода, Ошибка входа / бесконечная загрузка, Телепортация / Факторы: Задержка, Потери - У кого: Весь сервер, Один регион или провайдер / Когда: При наплыве игроков, Изредка, случайно - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Описать профиль игрового трафика (порты, размер пакетов, пакеты в секунду) и передать команде инфраструктуры, держать UDP-пакеты не больше 1 200 байт. - Команда инфраструктуры, задачи: Настроить правила защиты под профиль игрового трафика, использовать региональные узлы очистки, уменьшить размер TCP-пакетов на участке туннеля (MSS clamping), проверять ложные срабатывания по доле неудачных подключений в разрезе регионов и провайдеров. - Цифры для ориентира: Если узел очистки в той же стране, добавляются единицы ms, если в другой, от 30 до более чем 100 ms. Обычно в обход идёт только входящий трафик, а ответы сервера уходят напрямую. Если очищенный трафик возвращается через туннель, уменьшается и максимальный размер пакета (MTU), и это может привести к тому, что пропадают только большие пакеты. - На графике: Ступенька вверх с определённого момента (RTT (пинг), доля неудачных подключений по регионам и провайдерам) - Где смотреть: Наложить на одну временную шкалу записи о включении и выключении перенаправления (очистки) в оборудовании или сервисе защиты, логи блокировок, график 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 · Время туда и обратно в зависимости от расположения узла: Сеул–регион Пусан 8 ms, Сеул–Токио 30 ms, Сеул–Сингапур 68 ms #### dc-lb-idle · Таймаут простоя балансировщика · Load balancer idle timeout Балансировщик нагрузки удаляет неактивное соединение через определённое время. Игра считает, что соединение живо, и в итоге получает дисконнект. - Почему → Следствие → На экране: Игрок какое-то время не отправляет ни одного пакета (окно чата, отошёл от компьютера) → Балансировщик удаляет неактивное соединение (типичные значения по умолчанию 60–350 с) → Дисконнект в момент, когда игрок снова начинает двигаться - Симптомы: Дисконнект / Факторы: Потери - У кого: Только у меня, Весь сервер / Когда: После бездействия - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка клиента, Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: слать хартбиты с интервалом не больше половины самого короткого таймаута простоя (за ALB с 60 с не реже раза в 30 с), при обрыве автоматически переподключаться. Сервер: отвечать на хартбиты и первым закрывать соединение, если их нет определённое время, подхватывать игрока по токену сессии. - Команда инфраструктуры, задачи: Проверить таймаут простоя балансировщиков на пути, сообщить значение команде разработки и при необходимости увеличить. - Цифры для ориентира: Значения по умолчанию: AWS ALB 60 с, NLB 350 с для TCP и 120 с для UDP, Azure Load Balancer 4 мин для TCP. Значения TCP у ALB и NLB можно изменить, а 120 с для UDP у NLB изменить нельзя. По истечении таймаута ALB закрывает и соединение со стороны сервера, а NLB удаляет его молча, и сервер часто об этом не знает. - На графике: Массовый обрыв соединений (число обрывов, время бездействия перед обрывом) - Где смотреть: Проверить настройку таймаута простоя у балансировщиков на пути и собрать для каждого оборвавшегося соединения время от последнего пакета до обрыва. Для AWS NLB смотреть также TCP_ELB_Reset_Count в CloudWatch (число RST, отправленных балансировщиком) - Подтверждает: Время бездействия у оборвавшихся соединений скапливается сразу после настроенного значения (ALB 60 с, NLB 350 с для TCP и т. д.), и проблема воспроизводится, если простоять без действий дольше этого времени и затем двинуться. У 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 с), по его истечении NLB просто перестаёт отслеживать соединение и на последующие данные отвечает RST, 120 с для UDP-потоков изменить нельзя - [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 reset включается отдельно - [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 и т. п.) → Запись о соединении, которое какое-то время неактивно, истекает, и последующие пакеты группа безопасности молча отбрасывает → После отлучки игрок двигается, но ответа нет, затем дисконнект. Серверная программа долго ничего не замечает - Симптомы: Дисконнект / Факторы: Потери - У кого: Только у меня, Весь сервер / Когда: После бездействия - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка клиента, Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: слать хартбиты с интервалом не больше половины самого короткого таймаута простоя (при 350 с для TCP не реже раза в 175 с, при 180 с для UDP-потока не реже раза в 90 с), при обрыве автоматически переподключаться. Сервер: отвечать на хартбиты и первым закрывать соединение, если их нет определённое время, подхватывать игрока по токену сессии. - Команда инфраструктуры, задачи: Проверить время отслеживания соединений на инстансе (TcpEstablishedTimeout) и при необходимости увеличить (для UDP максимум 180 с, больше нельзя), рассмотреть конфигурацию группы безопасности без отслеживания (игровой порт открыт для всех адресов, исходящие правила разрешают всё. Соединения через NLB всё равно отслеживаются), при переходе на новое поколение инстансов проводить тест с бездействием. - Цифры для ориентира: В AWS типы инстансов Nitro v6 по умолчанию удаляют запись о неактивном TCP-соединении через 350 с (остальные типы через 5 дней). Для UDP по умолчанию 180 с для потоков с многократным обменом запросами и ответами (stream) и 30 с для потоков в одну сторону или с единственной парой запрос–ответ. - На графике: Массовый обрыв соединений (число обрывов, время бездействия перед обрывом) - Где смотреть: Проверить настройку времени отслеживания соединений на инстансе и правила группы безопасности (создают ли они отслеживание), собрать время бездействия у оборвавшихся соединений. Сразу после обрыва посмотреть на сервере через ss -tnoi, остаётся ли соединение в ESTABLISHED с работающим таймером повторной передачи (timer:(on,…)) и растущим backoff - Подтверждает: Время бездействия у оборвавшихся соединений скапливается сразу после 350 с для TCP, 180 с для UDP-потока, 30 с для UDP в одну сторону, а сокет на стороне сервера, не заметив обрыва, остаётся в 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 с, stream 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 · timer:(on,…) в выводе -o означает таймер повторной передачи, backoff в выводе -i показывает, сколько раз ожидание перед повтором удваивалось #### dc-nat-gateway · Лимиты соединений и портов облачного NAT-шлюза · Cloud NAT gateway connection / port limits Исходящие соединения серверов из частной подсети во внешний мир (авторизация на платформе, платежи, внешние API) проходят через NAT-шлюз, который подменяет адрес и порт. Если одновременных соединений к одной цели больше, чем позволяет лимит портов шлюза, новые соединения не устанавливаются. - Почему → Следствие → На экране: Серверы открывают много коротких соединений к одному внешнему адресу, например к авторизации на платформе или платёжному сервису, или долго держат соединения открытыми → NAT-шлюз не может выделить для этой цели ещё один исходный порт, и новое соединение не устанавливается → В самой игре всё нормально, но функции, которые обращаются наружу (вход, платежи, выдача наград), не работают или тормозят (ошибка входа / бесконечная загрузка, съеденные действия / роллбэк) - Симптомы: Ошибка входа / бесконечная загрузка, Съеденные действия / роллбэк / Факторы: Потери, Задержка - У кого: Только одна функция, Весь сервер / Когда: Сразу после входа или техработ, Вечерний пик, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Переиспользовать соединения к внешним API (HTTP keep-alive, пул соединений) и не открывать новое соединение на каждый запрос, для неактивных соединений в пуле слать keepalive чаще таймаута простоя NAT (350 с у AWS) или закрывать их первыми, при ошибках повторять с растущим интервалом и случайным разбросом, записывать долю ошибок и задержку по каждому внешнему вызову. - Команда инфраструктуры, задачи: Добавить IP-адреса на NAT-шлюз (к публичному NAT-шлюзу AWS по умолчанию можно привязать только 2 Elastic IP, для большего нужно запросить повышение квоты), разделить шлюзы по зонам доступности и подсетям, поставить алерты на метрики ошибок выделения портов (AWS ErrorPortAllocation, Failed в Azure SNAT Connection Count, OUT_OF_RESOURCES в Google Cloud dropped_sent_packets_count), в Google Cloud NAT увеличить минимум портов на VM или включить динамическое выделение портов. - Цифры для ориентира: AWS NAT Gateway с одного IP-адреса открывает до 55 000 одновременных соединений к одной цели (IP, порт, протокол), а добавляя IP (до 8), этот предел можно поднять. Соединение, молчавшее 350 с, удаляется, и на последующие пакеты по нему возвращается RST. У Azure NAT Gateway на один публичный IP приходится 64 512 портов SNAT (не больше 16 IP). Google Cloud NAT делит 64 512 портов одного NAT IP между VM, а минимум портов на VM по умолчанию 64 (статическое выделение), поэтому при настройках по умолчанию одна VM обычно может держать к одной цели не больше 64 одновременных соединений. - На графике: Упор в лимит (плато) (число одновременных соединений NAT-шлюза, число ошибок выделения портов) - Где смотреть: Сопоставить с моментами ошибок внешних вызовов на игровых серверах метрики NAT-шлюза в CloudWatch для AWS: ErrorPortAllocation, ActiveConnectionCount, PacketsDropCount (в Azure SNAT Connection Count с фильтром по состоянию Failed и Dropped Packets, в Google Cloud dropped_sent_packets_count с reason OUT_OF_RESOURCES) - Подтверждает: В моменты ошибок внешних вызовов ErrorPortAllocation (в Azure SNAT Connection Count в состоянии Failed, в Google Cloud отбрасывания OUT_OF_RESOURCES) больше 0, а ошибки сосредоточены на вызовах к одной-двум целям с большим числом соединений, например к серверам авторизации или платежей - Опровергает: Если ошибок выделения портов 0, а connect на игровом сервере завершается с EADDRNOTAVAIL и число TIME_WAIT близко к размеру диапазона эфемерных портов, это «Исчерпание эфемерных портов в межсерверных соединениях». Если соединение устанавливается, но ответ медленный, это «Зависимость от внешних сервисов» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Причина «Исчерпание эфемерных портов в межсерверных соединениях» описывает нехватку портов на одном сервере, а здесь лимит действует на 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 · 55 000 одновременных соединений к одной цели (IP, порт и протокол назначения) на один IPv4-адрес, предел поднимается добавлением IP до 8 (у публичного NAT-шлюза по умолчанию 2 Elastic IP, больше по запросу на повышение квоты); полоса автоматически растёт с 5 до 100 Gbps, а производительность с 1 до 10 млн пакетов в секунду, сверх этого предела пакеты отбрасываются - [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, рекомендуется keepalive чаще 350 с, при достижении лимита соединений добавить шлюзы по зонам доступности и IP или сократить число соединений - [Source Network Address Translation (SNAT) with Azure NAT Gateway](https://learn.microsoft.com/en-us/azure/nat-gateway/nat-gateway-snat) · Microsoft Azure · 64 512 портов SNAT на один публичный IP (не больше 16 IP), каждому соединению к одной цели нужен отдельный порт, закрытый порт проходит период охлаждения перед повторным использованием для той же цели - [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 по 64 512 портов для TCP и для UDP, минимум портов на 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 · Перекос балансировки и ошибки health check · LB imbalance, bad health checks Соединения скапливаются на одном сервере, или балансировщик продолжает отправлять игроков на уже упавший сервер. - Почему → Следствие → На экране: Правило распределения не подходит, или health check (проверка работоспособности) не видит реального состояния → Перегружен только один сервер, или подключения идут на упавший сервер → Только в части каналов или у части игроков слоумо, ошибка входа или бесконечная загрузка - Симптомы: Слоумо, Ошибка входа / бесконечная загрузка / Факторы: Остановка, Потери - У кого: Одна локация или канал / Когда: Сразу после входа или техработ, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Реализовать health check, который отвечает на запрос балансировщика по реальному состоянию игры (идут ли тики, есть ли соединение с БД), и сообщать заодно нагрузку сервера. - Команда инфраструктуры, задачи: Перевести health check на проверку реального ответа игры, распределять по нагрузке серверов, отслеживать разницу в числе соединений между серверами. - На графике: Высоко только у некоторых (число соединений и загрузка CPU по серверам) - Где смотреть: Наложить на один график число соединений (ss -s) и загрузку CPU каждого сервера за балансировщиком и сравнить статус health check целей на балансировщике (в AWS HealthyHostCount и UnHealthyHostCount в CloudWatch) с реальным состоянием игровых серверов - Подтверждает: У одного-двух серверов число соединений и CPU намного выше, чем у остальных, или сервер с остановившимися тиками числится «исправным» и продолжает принимать новые подключения - Опровергает: Если соединения распределены по серверам ровно, а тормозит только один канал, дело в нагрузке внутри этого канала («Перегрузка однопоточной локации (хотспот)») - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Реальные инциденты: aws-2025 - Источники: - [Load Balancing in the Datacenter](https://sre.google/sre-book/load-balancing-datacenter/) · Google · При простом round robin загрузка CPU между задачами расходится до 2 раз, взвешенное распределение, при котором бэкенд передаёт свою нагрузку в ответах и health check, состояние lame duck, в котором бэкенд просит больше не присылать ему запросы - [Health checks for Network Load Balancer target groups](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/target-group-health-checks.html) · AWS · Health check по умолчанию раз в 30 с, после 2 неудач цель исключается, UDP-сервисы проверяются через TCP или HTTP health check, поэтому рекомендуется настроить проверку так, чтобы она отражала реальное состояние сервиса - [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 Когда несколько серверов в один и тот же момент разом отправляют пакеты тысячам игроков, маленький буфер порта коммутатора, где сходится этот трафик, переполняется меньше чем за 1 ms. - Почему → Следствие → На экране: Появление мирового босса, массовое умение или совпавшие во времени тики нескольких серверов, и всё отправляется разом → Буфер (от сотен KB до нескольких MB на порт) там, где несколько портов сходятся в один или быстрый порт переходит в медленный, мгновенно заполняется → Часть пакетов отбрасывается, у многих игроков одновременно телепортация и съеденные умения - Симптомы: Телепортация, Съеденные действия / роллбэк / Факторы: Потери - У кого: Одна локация или канал / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Сетевая инфраструктура, Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Распределять отправку равномерно внутри тика (пейсинг), немного сдвигать начало тика на разных серверах. - Команда инфраструктуры, задачи: Сеть: коммутаторы с большими буферами, распределение трафика (разносить серверы по разным коммутаторам и портам), мониторинг счётчиков отбрасывания по портам коммутаторов. Серверы и ОС: ограничить общую скорость отправки сервера (шейпер tc в Linux). - Цифры для ориентира: Порт 10 Gbps за 1 ms может отправить около 1,25 MB. Если трафик двух портов одновременно сходится в один, каждую миллисекунду в очереди прибавляется 1,25 MB. Даже при средней загрузке 10% за секунду на интервале 1 ms порт может переполняться. - На графике: Растёт вслед за онлайном и нагрузкой (выходные отбрасывания на портах коммутатора) - Где смотреть: Собирать с минимально возможным интервалом счётчики выходных отбрасываний (ifOutDiscards, на некоторых устройствах output drops) на портах коммутатора, к которым подключены серверы, и на портах, где сходится их трафик, и сопоставлять с моментами появления босса и массовых боёв. На графиках средней загрузки за 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 портов делят 4 MB, один порт может занять около 700 KB), если несколько потоков на короткое время сходятся в один порт, возникают потери - [RFC 2863: The Interfaces Group MIB](https://www.rfc-editor.org/rfc/rfc2863) · IETF · ifOutDiscards: число пакетов, которые не были отправлены и отброшены без ошибок, например чтобы освободить место в буфере #### dc-uplink · Перегрузка линии связи ЦОД · Uplink saturation Если раздача патчей, отправка логов и бэкапы идут по той же линии, что и игра, линия забивается. - Почему → Следствие → На экране: Крупные передачи данных занимают ту же линию → Растут очередь на линии и потери → Рост пинга и телепортация на всём сервере - Симптомы: Задержка ввода, Телепортация / Факторы: Задержка, Потери - У кого: Весь сервер / Когда: С постоянным периодом, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Сеть: приоритет игровому трафику (QoS), отдельная линия для крупных передач, алерт на загрузку линии. Серверы и ОС: ограничить скорость бэкапов, отправки логов и деплоя и запускать их в часы низкой нагрузки. - На графике: Упор в лимит (плато) (загрузка линии, RTT (пинг)) - Где смотреть: Наложить на одну временную шкалу загрузку интерфейса внешней линии ЦОД (аплинка), рассчитанную по 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 · Переключение сетевого оборудования на резерв (failover) · Network device failover Когда маршрутизатор или файрвол выходит из строя и трафик переключается на резервное устройство (failover), на эти несколько секунд у всех фриз. - Почему → Следствие → На экране: Из-за отказа или обслуживания трафик переключается на резервное устройство → Переключение занимает несколько секунд, а если состояние сессий не синхронизировано, соединения сбрасываются → Одновременный фриз у всех игроков сервера, массовые дисконнекты - Симптомы: Фриз, Дисконнект / Факторы: Потери - У кого: Весь сервер / Когда: Изредка, случайно - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера, Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: таймауты, которые переживают короткие обрывы (несколько секунд), при переподключении после обрыва подхватывать сессию по токену. Клиент: при обрыве автоматически переподключаться (со случайным разбросом интервала между попытками, чтобы игроки не возвращались все разом). - Команда инфраструктуры, задачи: Резервирование с общим состоянием соединений, обнаружение отказов через BFD меньше чем за 1 с, регулярные тесты переключения. - Цифры для ориентира: Если устройство сразу замечает отказ, примерно 1–3 с. Если быстрого обнаружения отказов (BFD) нет и всё держится на стандартных таймерах BGP, маршрут может быть разорван 90–180 с, пока соседнее устройство не заметит отказ. - На графике: Массовый обрыв соединений (число подключений, общий входящий и исходящий трафик сервера) - Где смотреть: Посмотреть журналы событий маршрутизаторов и файрволов (смена роли VRRP, падение сессий BFD и BGP, записи о переключении на резерв) и в то же время число подключений и общий трафик серверов - Подтверждает: В момент переключения по журналу устройства трафик всех серверов за ним на несколько секунд падает до 0 или число подключений падает одновременно - Опровергает: Если подключения упали только на одном сервере, это «Падение сервера» или «Проблемы драйвера и прошивки NIC». Если журнал устройства чист, а остановилась одна облачная виртуальная машина, это «Обслуживание облачного хоста и живая миграция» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 · Если полагаться только на keepalive в BGP, сходимость медленная, если сразу реагировать на падение линка и разрывать сессию, отказ обнаруживается за миллисекунды и маршруты быстро сходятся заново - [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), на сервере crc в RX errors из ip -s -s link (статистика ядра rx_crc_errors) - Подтверждает: Ошибки 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 · Анализ 350 000 линков в ЦОД: повреждения пакетов вызывают неисправные оптические модули, повреждённое волокно и грязные коннекторы, доля повреждений постоянна и не зависит от загрузки, проблемные линки выводят и ремонтируют, сохраняя нужное число путей - [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) блокирует файрвол, и отправитель о нём не знает → Фриз и затем дисконнект только при открытии больших экранов вроде инвентаря или списка персонажей - Симптомы: Фриз, Дисконнект, Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Один регион или провайдер, Только у меня / Когда: При определённом действии, Сразу после входа или техработ - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка сервера - Команда разработки, задачи: Чтобы снизить размер на стороне сервера, задать в сокете максимальный размер сегмента TCP_MAXSEG (одного дробления сообщений в игровом коде недостаточно), держать UDP-пакеты не больше 1 200 байт. - Команда инфраструктуры, задачи: Сеть: уменьшить размер TCP-пакетов на участке туннеля (MSS clamping), разрешить уведомления о превышении размера (ICMP) на файрволах и в сетевых ACL облака. Серверы и ОС: разрешить уведомления о превышении размера (ICMP) и в файрволе сервера, и в облачной группе безопасности, включить в ядре сервера определение MTU через tcp_mtu_probing=1 (последняя страховка, которая срабатывает, только когда соединение уже простояло несколько секунд). - Цифры для ориентира: Обычно 1 500 байт, после туннеля около 1 400. - На графике: Высоко только у некоторых (дисконнекты по регионам и провайдерам, ошибки больших ответов) - Где смотреть: С ПК игрока с проблемой пинговать сервер с флагом запрета фрагментации (DF), меняя размер. В Windows ping /f /l 1472 SERVER_IP, в Linux ping -M do -s 1472 SERVER_IP (1 472 = MTU 1 500 минус 20 байт заголовка IP и 8 байт заголовка ICMP). Уменьшая размер, найти максимальный, который проходит, и проверить, разрешают ли группа безопасности и файрвол на стороне сервера ICMP-уведомления о превышении размера (Fragmentation Needed) - Подтверждает: Маленький пинг проходит, а пинг с DF размером 1 472 байта нет (ответа нет или приходит ошибка о необходимости фрагментации), и максимальный проходящий размер мал, около 1 400. У игроков из одного региона фриз только при открытии больших экранов - Опровергает: Если и пинг с DF размером 1 472 байта проходит, проблема с MTU пути исключается. Если не проходит даже маленький пинг, заблокирован сам ICMP, и этим способом ничего определить нельзя - Чем проверить: Проверка на стороне игрока - Источники: - [RFC 2923: TCP Problems with Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc2923) · IETF · Если файрвол блокирует ICMP (Fragmentation Needed), определение MTU пути не срабатывает и постоянно пропадают только большие пакеты (чёрная дыра), а пинг и мелкий обмен работают, поэтому диагностировать трудно - [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 определение MTU пути для TCP обычно выключено и включается, когда обнаружена чёрная дыра ICMP - [ping(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ping.8.html) · iputils · -M do ставит флаг DF и не отправляет пакеты больше MTU пути, -s задаёт размер данных (по умолчанию 56 байт плюс 8 байт заголовка ICMP) - [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 отправляет прерывания о приходе пакетов только на одно ядро CPU, это ядро становится узким местом. - Почему → Следствие → На экране: Одна очередь приёма, или выключен RSS, который распределяет пакеты по нескольким ядрам → Одно ядро загружено на 100% и не успевает забирать пакеты → При наплыве игроков потери и задержка на всём сервере (телепортация, задержка ввода) - Симптомы: Телепортация, Откидывание назад, Задержка ввода / Факторы: Потери, Задержка - У кого: Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Настроить RSS (распределяет NIC) и RPS (распределяет ядро ОС), распределить прерывания по нескольким ядрам, для UDP включить распределение по очередям с учётом портов (rx-flow-hash udp4 sdfn в ethtool -N), разнести ядра обработки прерываний и ядра потоков игрового тика, следить за %soft по ядрам. - Цифры для ориентира: Через сетевой стек ядра ОС одно процессорное ядро успевает обработать примерно сотни тысяч пакетов в секунду, в зависимости от размера пакетов и настроек. Если в загрузке по ядрам доля обработки приёма (%soft в mpstat) сосредоточена на одном ядре, причина именно в этом. - На графике: Упор в лимит (плато) (%soft по ядрам, входящие пакеты в секунду) - Где смотреть: Посмотреть через mpstat -P ALL 1 %soft (долю времени на обработку программных прерываний) по ядрам, через /proc/interrupts, на какие ядра идут прерывания каждой очереди NIC, через ethtool -l число очередей, через ethtool -S число пакетов по очередям (названия счётчиков зависят от драйвера) - Подтверждает: У одного ядра %soft держится около 100%, остальные простаивают, прерывания и пакеты скапливаются в одной очереди. С этого момента число входящих пакетов в секунду перестаёт расти - Опровергает: Если %soft ровно распределён по ядрам, причина в другом. Если CPU простаивает, а потери есть, это «Превышен лимит PPS в облаке» или «Слишком маленький кольцевой буфер» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Даже при нескольких очередях трафик скапливается в одной, если почти весь он приходит с немногих адресов, например от шлюзов или прокси. Для 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 · Замеры, в которых при одной очереди приёма на одно ядро это ядро упиралось примерно в 350–430 тыс. пакетов в секунду, случай, когда NIC хешировал UDP только по IP-адресам и весь трафик шёл в одну очередь - [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), следить за счётчиками отбрасываний (rx_missed_errors и другие в ethtool -S, названия зависят от драйвера). - Цифры для ориентира: При потоке 1 млн пакетов в секунду 1 024 слота заполняются примерно за 1 ms. Достаточно CPU один раз опоздать за это время, и буфер переполнится. У большинства NIC буфер можно увеличить до нескольких тысяч слотов. - На графике: Случайные всплески (счётчик отбрасываний при приёме в NIC) - Где смотреть: Собирать с коротким интервалом счётчики отбрасываний при приёме из ethtool -S (rx_missed_errors, rx_fifo_errors и т. п., названия зависят от драйвера) и missed из ip -s -s link, через ethtool -g проверить текущий и максимальный размер кольцевого буфера - Подтверждает: В моменты всплесков счётчики отбрасываний растут, а текущий размер буфера намного меньше максимального. После увеличения буфера отбрасываний становится меньше - Опровергает: Если счётчики отбрасываний не меняются, а потери есть, дело на следующем этапе в ядре ОС («Нехватка буферов сокетов в ядре») или на сетевом участке. Если %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 копит пакеты и сообщает о них разом, пакеты задерживаются на время накопления. - Почему → Следствие → На экране: NIC копит пакеты определённое время или до определённого числа и только потом сообщает о них → Пока идёт накопление, пакеты ждут → Небольшой рост задержки. Обычно он мал, но при чрезмерных настройках доходит до миллисекунд - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Весь сервер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Включить адаптивное объединение, подобрать значения под игровой сервер (ethtool -C). - Цифры для ориентира: Обычно от десятков до сотен µs. Для игр это, как правило, пренебрежимо мало, но при чрезмерных настройках вырастает до миллисекунд. - На графике: Высоко с самого начала (время туда и обратно внутри одного ЦОД) - Где смотреть: Посмотреть текущие настройки объединения через 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 (около 67 ms), чем больше значение, тем больше задержка приёма - [ethtool(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ethtool.8.html) · ethtool · Настройки adaptive-rx, rx-usecs и rx-frames в ethtool -C #### nic-cloud-pps · Превышен лимит PPS в облаке · Cloud PPS / bandwidth allowance У каждого типа облачного сервера есть лимиты пакетов в секунду и пропускной способности, и всё сверх лимита молча отбрасывается. - Почему → Следствие → На экране: Онлайн растёт, и число пакетов в секунду превышает лимит инстанса → Облачная сеть отбрасывает превышение → Потери непонятного происхождения, телепортация, съеденные умения. При этом у CPU сервера есть запас - Симптомы: Телепортация, Съеденные действия / роллбэк / Факторы: Потери - У кого: Весь сервер / Когда: При наплыве игроков, Вечерний пик - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера, Внешние стороны · Внешние стороны - Команда разработки, задачи: Объединять пакеты (сообщения одного тика в одном пакете), не отправлять часто совсем мелкие пакеты. - Команда инфраструктуры, задачи: Проверять счётчики превышения лимитов (в AWS pps_allowance_exceeded, conntrack_allowance_exceeded и др.) и ставить на них алерты, переходить на более крупный инстанс, лимит отслеживания соединений обходить конфигурацией группы безопасности без отслеживания. - Внешние стороны, задачи: Запросить у облачного провайдера лимиты пакетов в секунду и отслеживания соединений для каждого типа инстансов. - Цифры для ориентира: Лимиты зависят от размера инстанса, а лимит пакетов в секунду часто не раскрывают. «До 10 Gbps» у небольших инстансов означает скорость в режиме burst, доступную только пока есть кредиты (обычно 5–60 мин), а базовая скорость намного ниже. - На графике: Упор в лимит (плато) (пакеты в секунду, счётчики превышения allowance) - Где смотреть: Собирать с коротким интервалом счётчики ENA из ethtool -S: pps_allowance_exceeded, bw_in_allowance_exceeded, bw_out_allowance_exceeded, conntrack_allowance_exceeded и смотреть их вместе с числом пакетов в секунду. Можно также отправлять эти счётчики в CloudWatch через агент и ставить на них алерты - Подтверждает: В момент потерь растут счётчики превышения allowance, а число пакетов в секунду упирается в одно значение. У CPU сервера есть запас - Опровергает: Если счётчики превышения не меняются, причина в другом. Если %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 · «До N Gbps» у инстансов с 16 vCPU и меньше означает burst за счёт кредитов сетевого ввода-вывода (обычно 5–60 мин), когда кредиты заканчиваются, полоса возвращается к базовой #### nic-saturate · Исчерпание пропускной способности NIC · NIC bandwidth saturation Если карта на 1 Gbps или 10 Gbps загружена до предела, очередь отправки растёт, и в итоге пакеты отбрасываются. - Почему → Следствие → На экране: Из-за роста рассылки объём отправки доходит до предела карты → Очередь отправки растёт, а при переполнении пакеты отбрасываются → Задержка и потери на всём сервере (задержка ввода, телепортация) - Симптомы: Задержка ввода, Телепортация / Факторы: Задержка, Потери - У кого: Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Сократить объём отправки (фильтрация по зоне интереса AOI, сжатие, отправка только изменений). - Команда инфраструктуры, задачи: Нарастить сетевые карты (более быстрая NIC, в облаке более крупный инстанс), поставить алерт на загрузку NIC. - На графике: Упор в лимит (плато) (объём отправки NIC, отбрасывания при отправке) - Где смотреть: Сравнить txkB/s и %ifutil (загрузку относительно скорости интерфейса) из sar -n DEV 1 со скоростью NIC и полосой инстанса и заодно посмотреть TX dropped в ip -s link - Подтверждает: Объём отправки выходит на плато около предела 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 · rxkB/s, txkB/s и %ifutil (загрузка относительно скорости интерфейса) в -n DEV #### nic-noisy · Накладные расходы виртуализации и шумные соседи · Noisy neighbors in virtualization Если другие виртуальные машины на том же физическом сервере активно используют сеть и CPU, обработка на нашем сервере нерегулярно запаздывает. - Почему → Следствие → На экране: Другие виртуальные машины на том же физическом сервере потребляют много ресурсов → Обработка пакетов на нашей виртуальной машине нерегулярно запаздывает → Без явной причины время от времени появляется джиттер (неравномерность интервалов между пакетами) и микрофризы - Симптомы: Микрофризы / Факторы: Джиттер - У кого: Весь сервер / Когда: Изредка, случайно - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Внешние стороны · Внешние стороны - Команда инфраструктуры, задачи: Выделенные хосты, инстансы с гарантированной производительностью, инстанс с постоянным джиттером остановить и запустить снова, чтобы он переехал на другой хост. - Внешние стороны, задачи: Сообщить облачному провайдеру о проблемном хосте. - На графике: Случайные всплески (джиттер времени туда и обратно внутри ЦОД, %steal) - Где смотреть: Непрерывно пинговать другой сервер в том же ЦОД и записывать джиттер времени туда и обратно, сравнить его вместе с %steal из mpstat с другими инстансами той же конфигурации - Подтверждает: Только у этого инстанса джиттер времени туда и обратно или %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 месяцев) около 30 с, а живая миграция обычно не дольше 5 с. Azure Scheduled Events предупреждает о такой остановке (Freeze) минимум за 15 мин. Но если оборудование хоста внезапно отказывает, восстановление начинается сразу, без предупреждения. - На графике: Провал, затем пачка (пакеты сервера на приём и отправку, интервал тика) - Где смотреть: Сверить момент остановки с записями провайдера. В Google Cloud это compute.instances.migrateOnHostMaintenance в журнале аудита, в AWS запланированные события в describe-instance-status и AWS Health, в Azure Microsoft.Compute/virtualMachines/liveMigration/action в журнале действий (Activity Log) и момент, когда метрика доступности VM (VmAvailabilityMetric) упала до 0. Внутри сервера посмотреть, есть ли пробел в метриках и логах на время остановки и прыгнули ли часы сразу после неё (журнал синхронизации времени) - Подтверждает: Момент остановки всего сервера совпадает с записанным у провайдера временем обслуживания или миграции, и на эти несколько секунд все метрики и логи внутри сервера пустые - Опровергает: Если в записях провайдера ничего нет, а короткие остановки часто повторяются, это «CPU steal (виртуальная машина)». Если в логе ядра есть записи о сбросе NIC, это «Проблемы драйвера и прошивки NIC» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: AWS предупреждает об обслуживании через запланированные события (scheduled events). system-reboot означает перезагрузку с переносом на новый хост, system-maintenance означает, что обслуживание сети или питания может ненадолго повлиять на инстанс. Даже если остановка длится несколько секунд, клиенты, не получившие за это время ACK на отправленные серверу пакеты, удваивают ожидание перед каждым повтором, поэтому TCP-соединение может оставаться замершим и после того, как сервер оживёт (см. причину «TCP RTO и экспоненциальный backoff»). После пробуждения часы прыгают (см. причину «Скачок системных часов (шаговая коррекция NTP)»), а health check балансировщика может не пройти, и сервер ненадолго исключат из балансировки. Инстансы, которые нельзя перенести (например, bare metal в Google Cloud), при обслуживании останавливаются или перезапускаются. - Источники: - [Live migration process during maintenance events](https://cloud.google.com/compute/docs/instances/live-migration-process) · Google Cloud · Остановка при живой миграции обычно намного короче 1 с, за время остановки системные часы прыгают вперёд до 5 с, во время переноса ненадолго падает производительность диска, CPU, памяти и сети, VM без живой миграции при обслуживании выключаются (bare metal её не поддерживает) - [Query metadata server for maintenance event notices](https://cloud.google.com/compute/docs/metadata/getting-live-migration-notice) · Google Cloud · Значение метаданных maintenance-event меняется за 60 с до живой миграции (если VM настроена на живую миграцию и после прошлого обслуживания это значение хотя бы раз запрашивали) - [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 · Обслуживание без перезагрузки почти всегда останавливает VM меньше чем на 10 с, изредка (для обычных размеров не чаще раза в 18 месяцев) примерно на 30 с, живая миграция обычно не дольше 5 с, после остановки часы синхронизируются автоматически, долгие TCP-соединения могут рваться, а данные, отправленные остановленной VM, другая сторона повторяет с экспоненциальным backoff, и восстановление затягивается, health check балансировщика примерно за 10 с признаёт VM неисправной, проверка по Microsoft.Compute/virtualMachines/liveMigration/action в журнале действий и по VmAvailabilityMetric, которая на время остановки падает до 0, время применения выбирается через 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 Из-за ошибки драйвера или сбоя какой-то функции карта зависает и перезапускается, и на это время весь приём и отправка прекращаются. - Почему → Следствие → На экране: Ошибка драйвера, сбой функций offload → NIC зависает и перезапускается (несколько секунд) → У всех на этом сервере одновременно фриз, затем телепортация или дисконнект - Симптомы: Фриз, Дисконнект / Факторы: Потери - У кого: Весь сервер / Когда: Изредка, случайно, Чем дольше без перезапуска - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Отслеживать в логе ядра записи «transmit queue … timed out» и «Link is Down» и ставить на них алерты, обновить драйвер и прошивку, отключить проблемную функцию (offload и т. п.). - На графике: Провал, затем пачка (пакеты сервера на приём и отправку) - Где смотреть: Найти в логе ядра через dmesg записи «NETDEV WATCHDOG … transmit queue N timed out», сброс драйвера, «Link is Down» и «Link is Up» и посмотреть число пакетов сервера на приём и отправку в это время - Подтверждает: В момент остановки в логе ядра есть таймаут очереди отправки или падение и подъём линка, и на эти несколько секунд число принятых и отправленных пакетов падает до 0 - Опровергает: Если лог ядра чист и порт на коммутаторе в порядке, это остановка процесса игрового сервера («Полная пауза GC на сервере», «Дедлок») или «Переключение сетевого оборудования на резерв (failover)» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 · Если очередь отправки зависла, watchdog ядра пишет «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, чтобы отключать функции offload по одной #### nic-offload · Ожидание объединения пакетов в GRO/LRO · GRO/LRO batching GRO и LRO объединяют несколько пакетов в один, чтобы снизить нагрузку на CPU. При некоторых настройках маленький игровой пакет ненадолго ждёт следующий, чтобы объединиться с ним. - Почему → Следствие → На экране: NIC или ядро ОС объединяет пришедшие пакеты и обрабатывает их вместе → Если включено аппаратное объединение (LRO) или задано время ожидания объединения, пакет ненадолго ждёт следующий → Небольшой рост задержки (обычно не больше нескольких десятков µs) - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Весь сервер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Подстроить под игровой трафик (выключить LRO, проверить настройку времени ожидания объединения), эффект обычно мал, поэтому проверять после других причин. - На графике: Высоко с самого начала (время туда и обратно внутри одного ЦОД) - Где смотреть: Проверить состояние lro и gro через ethtool -k и значение gro_flush_timeout в настройках устройства в sysfs, сравнить время туда и обратно для маленьких пакетов внутри ЦОД до и после изменения - Подтверждает: LRO включён или gro_flush_timeout больше 0, и если выключить LRO или поставить 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 · Настройки gro и lro on|off в ethtool -K ### L7 ОС сервера (ядро) (причин: 14) #### so-backlog · Переполнение очереди подключений (backlog) · Listen backlog / SYN queue overflow Когда сразу после техработ одновременно подключаются десятки тысяч игроков, очередь подключений ядра (backlog) переполняется и попытки подключения отбрасываются. - Почему → Следствие → На экране: С окончанием техработ подключения приходят быстрее, чем игровой сервер успевает принимать их через accept → Очередь подключений ядра (backlog: меньшее из значения, которое код сервера передал в listen, и предела ядра) заполнена → Попытки подключения отбрасываются, повторы идут один за другим: ошибка входа / бесконечная загрузка - Симптомы: Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Весь сервер / Когда: Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: увеличить значение, которое код передаёт в listen, не давать потоку, принимающему подключения, останавливаться на другой работе, сделать очередь на вход. Клиент: увеличивать интервал между повторами (со случайным разбросом). - Команда инфраструктуры, задачи: Увеличить somaxconn в ядре (эффект будет, только если поднять и его, и значение listen в коде сервера), держать SYN cookies включёнными, следить за числом переполнений (TcpExtListenOverflows в nstat). - Цифры для ориентира: Предел ядра Linux (somaxconn) начиная с версии 5.4 по умолчанию равен 4 096 (до этого 128), но если код сервера передаёт в listen меньшее значение, лимитом становится оно. Когда очередь заполнена, Linux молча, без ошибки, отбрасывает запросы на подключение. ОС клиента повторяет запрос несколько раз, начиная через 1 секунду, поэтому для игрока это выглядит как долгая загрузка без сообщения «Ошибка подключения». Сервер на Windows возвращает отказ, и клиент сразу видит «Ошибка подключения». - На графике: Всплеск сразу после входа или техработ (число переполнений очереди подключений (ListenOverflows), число попыток подключения) - Где смотреть: Посмотреть прирост TcpExtListenOverflows и TcpExtListenDrops в nstat -az и сравнить через 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 · Backlog в listen, превышающий 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: сколько раз запрос на подключение (SYN) был отброшен из-за заполненной очереди accept. Вместе с ним растёт и 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 · Значения Recv-Q и Send-Q в ss: у слушающего сокета это число подключений, ждущих accept, и лимит backlog, у подключённого сокета это байты, ещё не прочитанные приложением, и отправленные байты без ACK #### so-fd · Лимит файловых дескрипторов · File descriptor limit (ulimit) Каждому соединению нужен файловый дескриптор (fd, номер, который ОС присваивает открытому файлу или сокету), а число fd, которые может открыть один процесс, ограничено. - Почему → Следствие → На экране: Онлайн достигает лимита файловых дескрипторов процесса → Сервер не может принять новые соединения (Too many open files). Заодно не открываются файлы логов и соединения с БД → Начиная с определённого числа игроков больше никто не может войти: ошибка входа / бесконечная загрузка - Симптомы: Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Весь сервер / Когда: Сразу после входа или техработ, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Надёжно закрывать сокет при завершении соединения (чтобы не было утечки fd). Если accept завершается с EMFILE (не хватает fd), ненадолго перестать принимать подключения или принять подключение на заранее отложенный резервный fd и сразу закрыть (чтобы не тратить CPU на повторную обработку одного и того же уведомления о подключении). - Команда инфраструктуры, задачи: Проверить ulimit и настройки сервиса (LimitNOFILE в systemd), настроить алерт при приближении к лимиту. - Цифры для ориентира: В Linux, если отдельно не настроить сервис, лимит всё ещё часто равен 1 024. Для игровых серверов его обычно поднимают до десятков или сотен тысяч. В Windows такого низкого лимита по умолчанию нет. - На графике: Упор в лимит (плато) (число открытых fd процесса, онлайн) - Где смотреть: Посмотреть fd-nr (число открытых файловых дескрипторов) процесса игрового сервера через pidstat -v и лимит открытых файлов в /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 · Winsock в Windows ограничивает число сокетов только доступной памятью - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · fd-nr в выводе -v: число файловых дескрипторов, открытых процессом - [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). - Цифры для ориентира: Буфер приёма UDP в Linux по умолчанию около 208 KB. Даже маленький пакет занимает в памяти ядра намного больше своего реального размера, поэтому буфер заполняют уже десятки или сотни пакетов. На сервере, который принимает 100 000 пакетов в секунду, буфер переполняется, если принимающий поток остановится всего на несколько ms. - На графике: Случайные всплески (переполнения буфера приёма UDP (UdpRcvbufErrors)) - Где смотреть: Посмотреть прирост UdpRcvbufErrors в nstat -az и skmem в ss -uamn (rb: размер буфера приёма, d: число пакетов, отброшенных до попадания в сокет). Для TCP проверить в skmem из ss -tm, не дошла ли память очереди отправки (w) до размера буфера отправки (tb) - Подтверждает: В моменты всплесков трафика или остановок принимающего потока растёт UdpRcvbufErrors (или d у сокета), а rb около значения по умолчанию (примерно 208 KB). В 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. Ядро удваивает заданное значение - [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 · Буфер сокета по умолчанию определён как 256 пакетов по 256 байт с учётом накладных расходов sk_buff (SKB_TRUESIZE(256)×256). Даже маленький кадр учитывается как sk_buff+MTU (около 208 KB: расчётное значение для 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: RcvbufErrors и SndbufErrors в группе Udp - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · skmem в выводе -m: rb (размер буфера приёма), tb (размер буфера отправки), w (память очереди отправки), d (число пакетов, отброшенных до попадания в сокет) #### so-context · Избыток потоков и переключение контекста · Thread oversubscription, context switching Если потоков намного больше, чем ядер, заметная часть CPU уходит только на то, чтобы ОС запускала их по очереди. - Почему → Следствие → На экране: Потоков сотни или тысячи (например, свой поток на каждое соединение) → Растут затраты на переключение контекста (смену выполняемого потока) и число промахов кэша → CPU занят, но производительность низкая, тики неравномерные: микрофризы, слоумо - Симптомы: Микрофризы, Слоумо / Факторы: Остановка, Джиттер - У кого: Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Держать число потоков по числу ядер, перейти на асинхронный ввод-вывод (epoll, IOCP). - Команда инфраструктуры, задачи: Мониторить число переключений контекста и число потоков в очереди на выполнение (cs и r в vmstat). - Цифры для ориентира: Одно переключение контекста стоит несколько µs, а вместе с последующими промахами кэша ещё больше. - На графике: Растёт вслед за онлайном и нагрузкой (переключения контекста в секунду, число потоков в очереди на выполнение) - Где смотреть: Сравнить cs (переключения контекста в секунду) и r (число выполняемых или ждущих CPU) из vmstat 1 с числом ядер и посмотреть через pidstat -w -t добровольные (cswch/s) и принудительные (nvcswch/s) переключения контекста по потокам игрового сервера - Подтверждает: С ростом онлайна r поднимается намного выше числа ядер, cs тоже взлетает, а потоков с большим числом принудительных переключений контекста сотни - Опровергает: Если r не превышает числа ядер, причина в другом. Если много только добровольных переключений, потоки ждут блокировок или ввода-вывода («Конкуренция за блокировки», «Архитектура с блокирующим вводом-выводом») - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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, а число одновременно работающих потоков ограничивается по числу процессоров - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · cswch/s в выводе -w: добровольные переключения контекста, когда поток сам останавливается в ожидании ресурса. nvcswch/s: принудительные переключения, когда поток израсходовал квант времени. -t выводит данные по потокам #### so-steal · CPU steal (виртуальная машина) · CPU steal time Пока физический сервер (гипервизор) временно отдаёт процессорное время виртуальной машины другим виртуальным машинам (CPU steal), игровой сервер стоит. - Почему → Следствие → На экране: Другие виртуальные машины на том же хосте активно используют CPU → Виртуальная машина игрового сервера то и дело не получает CPU на время от единиц до десятков ms → Необъяснимые всплески времени тика: микрофризы, фризы - Симптомы: Микрофризы, Фриз / Факторы: Остановка - У кого: Весь сервер / Когда: Изредка, случайно - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Внешние стороны · Внешние стороны - Команда инфраструктуры, задачи: Мониторить steal (st в top и vmstat), использовать выделенные ядра или хосты, избегать burst-инстансов, которые замедляются, когда кончаются CPU-кредиты. Инстанс с постоянно высоким steal остановить и запустить заново, чтобы он переехал на другой хост. - Внешние стороны, задачи: Сообщить облачному провайдеру о хосте с постоянно высоким steal. - На графике: Случайные всплески (%steal, время тика сервера) - Где смотреть: Наложить %steal из mpstat -P ALL 1 на время тика сервера на одной временной оси - Подтверждает: В моменты всплесков времени тика скачет и %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: время, отнятое в виртуализированной среде на выполнение других операционных систем - [Standard mode for burstable performance instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-standard-mode.html) · AWS · Burst-инстансы тратят кредиты, чтобы работать выше базовой производительности, а когда кредиты заканчиваются, загрузка 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, то, израсходовав квоту в пределах периода (обычно 100 ms), он принудительно останавливается до конца периода (троттлинг). - Почему → Следствие → На экране: В Kubernetes или похожей системе контейнеру игрового сервера задан лимит CPU (limit) → В момент пиковых расчётов тика квота кончается, и контейнер стоит десятки ms до следующего периода → Средняя загрузка CPU низкая, но время тика периодически скачет: микрофризы, слоумо - Симптомы: Микрофризы, Слоумо / Факторы: Остановка, Джиттер - У кого: Весь сервер / Когда: При наплыве игроков, Изредка, случайно - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Подогнать число рабочих потоков под лимит CPU (чтобы рантайм не создавал потоки по числу всех ядер хоста). - Команда инфраструктуры, задачи: Задать лимит CPU с запасом или убрать его и выделить ядра, следить за числом троттлингов (nr_throttled). - Цифры для ориентира: Если на сервере с лимитом в 2 ядра одновременно работают 8 потоков, квота периода 100 ms расходуется за 25 ms, и следующие 75 ms сервер стоит. - На графике: Растёт вслед за онлайном и нагрузкой (число троттлингов (nr_throttled), время тика сервера) - Где смотреть: Смотреть прирост nr_throttled и throttled_usec (в cgroup v1 nr_throttled и throttled_time) в cpu.stat cgroup контейнера вместе со временем тика сервера - Подтверждает: Средняя загрузка CPU ниже лимита, но nr_throttled и throttled_usec постоянно растут, и их рост совпадает со всплесками времени тика - Опровергает: Если nr_throttled не растёт, причина в другом. Если CPU недодают самой виртуальной машине, это «CPU steal (виртуальная машина)» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [CFS Bandwidth Control](https://docs.kernel.org/scheduler/sched-bwc.html) · Linux kernel · Если квота на период израсходована, потоки стоят до следующего периода (троттлинг). Период по умолчанию 100 ms, статистика nr_throttled - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · cpu.max имеет формат «$MAX $PERIOD» (квота, период), значение по умолчанию «max 100000» (период 100 ms) - [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) и снижает частоту. Когда приходит пакет или срабатывает таймер, на пробуждение и подъём частоты уходит время, и к обработке даже маленьких пакетов добавляется задержка. - Почему → Следствие → На экране: Политика управления частотой в ОС (governor) или настройки питания в BIOS разрешают глубокие C-state и низкие частоты → Каждое пробуждение ядра из глубокого сна добавляет до сотен µs, а если частота зафиксирована на низком уровне, медленнее идёт сам расчёт тика → Обычно это почти незаметно, но при большом числе межсерверных вызовов задержки складываются, и в часы затишья ответ, как ни странно, приходит позже: задержка ввода. Если частота зафиксирована на низком уровне, при наплыве игроков тики отстают: слоумо - Симптомы: Задержка ввода, Слоумо / Факторы: Задержка, Джиттер, Остановка - У кого: Весь сервер / Когда: Всегда, Изредка, случайно - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Перевести настройки питания в BIOS в режим производительности, а governor в ОС в performance (scaling_governor в cpufreq). На серверах, чувствительных к задержке, ограничить глубокие C-state (профиль tuned latency-performance, /dev/cpu_dma_latency в PM QoS, параметр ядра intel_idle.max_cstate). После изменений сравнить RTT внутри ЦОД, джиттер времени тика и энергопотребление. - Цифры для ориентира: По таблице драйвера intel_idle в Linux 6.12 неглубокое состояние C1 у серверных CPU Intel требует для пробуждения 1–2 µs, а глубокое C6 от 133 µs (Skylake-SP) до 290 µs (Sapphire Rapids). Один раз это немного, но если запрос проходит через несколько серверов, задержки складываются. Чем дольше ожидаемый простой, тем более глубокое состояние выбирает ядро ОС, поэтому чаще это проявляется на ненагруженных серверах, куда пакеты приходят изредка. Универсальный governor powersave в cpufreq фиксирует самую низкую частоту из допустимого диапазона (одноимённый алгоритм intel_pstate регулирует частоту по нагрузке). - На графике: Высоко с самого начала (RTT внутри ЦОД, частота ядер) - Где смотреть: Посмотреть через cpupower monitor долю времени в каждом C-state и фактическую частоту по ядрам, проверить для каждого state в /sys/devices/system/cpu/cpu0/cpuidle/ поля name, latency (время пробуждения в µs) и usage, а также scaling_governor в cpufreq и текущий профиль через tuned-adm active - Подтверждает: Простаивающие ядра подолгу находятся в самом глубоком C-state или их частота держится около минимальной, а после перехода на governor performance и неглубокие C-state RTT и джиттер мелких запросов снижаются - Опровергает: Если после изменений разница не больше десятков µs, этой причиной можно пренебречь. Если всплески измеряются в ms, это «CPU steal (виртуальная машина)» или другой слой - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: На bare-metal-серверах в ЦОД смотрят и настройки питания в BIOS (прошивке), и настройки ОС. В облаке менять C-state и частоту из ОС можно только на некоторых типах инстансов, а в AWS настройки по умолчанию ориентированы на максимальную производительность, так что обычно их можно не трогать. Профиль tuned latency-performance в дистрибутивах семейства Red Hat ставит 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 · Время пробуждения серверных CPU Intel по 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 · Алгоритм powersave в intel_pstate, в отличие от универсального governor powersave, регулирует частоту по нагрузке (похоже на 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 · Управлять C-state и P-state из ОС можно только на некоторых типах инстансов, и их можно менять, чтобы снизить задержку. Настройки по умолчанию дают максимальную производительность и подходят для большинства задач. У Graviton частота фиксированная, и ОС ею не управляет #### so-oom · OOM killer · Out-of-memory killer Когда память заканчивается, Linux выбирает процесс, который занимает больше всего памяти, и принудительно его завершает. Обычно это игровой сервер. - Почему → Следствие → На экране: Память исчерпана из-за утечки или резкого роста потребления, либо контейнер упёрся в лимит памяти → Ядро принудительно завершает процесс игрового сервера → Дисконнект у всех на этом сервере одновременно, возможен роллбэк недавнего прогресса - Симптомы: Дисконнект, Съеденные действия / роллбэк / Факторы: Остановка - У кого: Весь сервер / Когда: Чем дольше без перезапуска, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Исправить утечки, задать верхнюю границу потребления памяти и при приближении к ней сохранять данные и штатно завершать работу. - Команда инфраструктуры, задачи: Настроить алерты по памяти, задать лимит памяти контейнера по фактическому потреблению, настроить очерёдность завершения процессов (oom_score_adj). - Цифры для ориентира: В журнале ядра (dmesg) остаётся запись «Out of memory: Killed process», а в Kubernetes виден статус OOMKilled. В Windows OOM killer нет, и там сервер чаще падает с ошибкой, когда не удаётся выделить память. - На графике: Массовый обрыв соединений (число подключений, расход памяти) - Где смотреть: Сопоставить с моментом обрыва подключений запись «Out of memory: Killed process» в dmesg, статус пода OOMKilled в Kubernetes или прирост oom_kill в memory.events в cgroup v2 - Подтверждает: В момент массового обрыва подключений есть запись о завершении процесса игрового сервера, а перед этим расход памяти дорос до лимита - Опровергает: Если записи OOM нет, а процесс завершился, смотреть лог падения и core dump, как в причине «Падение сервера» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 при достижении предела выделения (commit limit) выделение памяти с фиксацией (commit) завершается ошибкой, что может привести к сбою приложения или системы - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · oom_kill в memory.events: число процессов в этой cgroup, завершённых OOM killer #### so-reclaim · Остановки из-за освобождения и уплотнения памяти · Memory compaction / reclaim stalls (THP) Процесс останавливается, пока ОС уплотняет память (compaction), чтобы собрать большие страницы (huge pages), или освобождает память, чтобы пополнить запас свободной (reclaim). - Почему → Следствие → На экране: Свободной памяти становится мало, или механизм больших страниц (THP) запускает уплотнение памяти → Поток, запросивший память, ждёт, пока закончится освобождение или уплотнение → Нерегулярные остановки сервера (от единиц до сотен ms) - Симптомы: Фриз, Микрофризы / Факторы: Остановка - У кого: Весь сервер / Когда: Чем дольше без перезапуска, Изредка, случайно - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Сократить крупные выделения памяти во время работы (выделять заранее при запуске и переиспользовать). - Команда инфраструктуры, задачи: Разрешить большие страницы (THP) только там, где они нужны (madvise), поднять порог свободной памяти (vm.min_free_kbytes и др.). - На графике: Случайные всплески (время тика сервера, PSI памяти) - Где смотреть: Смотреть some и full в /proc/pressure/memory (доля времени, проведённого в ожидании памяти) и прирост compact_stall в /proc/vmstat вместе со временем тика сервера, проверить настройку /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: минимальный объём свободной памяти, который ядро держит в резерве (порог watermark) - [PSI - Pressure Stall Information](https://docs.kernel.org/accounting/psi.html) · Linux kernel · some в /proc/pressure/memory (доля времени, когда часть задач стояла в ожидании памяти) и full (доля времени, когда стояли все задачи) #### so-timejump · Скачок системных часов (шаговая коррекция NTP) · Wall-clock jump (NTP step) Если часы сервера разом переводятся на несколько секунд вперёд или назад, таймеры, завязанные на системные часы, срабатывают пачкой или замирают. - Почему → Следствие → На экране: Синхронизация времени резко переводит часы → Таймеры срабатывают пачкой или замирают, таймауты определяются неверно → Сбои баффов и кулдаунов, массовый дисконнект, перемотка - Симптомы: Перемотка, Дисконнект, Съеденные действия / роллбэк / Факторы: Остановка - У кого: Весь сервер / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Считать прошедшее время, таймауты и кулдауны по монотонным часам (monotonic clock), которые не скачут и не идут назад, а wall clock использовать только для отображения и записи в логи. - Команда инфраструктуры, задачи: Корректировать часы плавно (makestep в chrony только сразу после запуска), мониторить состояние синхронизации времени (расхождение часов). - Цифры для ориентира: ntpd переводит часы разом, если расхождение больше 0,128 с, а меньшее расхождение устраняет плавно, со скоростью, при которой на 1 секунду уходит чуть больше 30 минут. Популярный сейчас chrony с рекомендуемой настройкой (makestep) переводит часы разом лишь несколько раз сразу после запуска, а дальше корректирует плавно. Часы скачут и тогда, когда виртуальная машина ненадолго останавливается и снова возобновляет работу. - На графике: Случайные всплески (число срабатываний таймеров и обрывов, журнал коррекций часов) - Где смотреть: Найти в логе службы синхронизации времени записи о резкой коррекции часов и сопоставить их со временем сбоев. chrony пишет в syslog, если коррекция больше значения logchange (по умолчанию 1 с) - Подтверждает: В моменты сбоев баффов и кулдаунов, массовых дисконнектов и перемотки есть записи о коррекции часов, и величина коррекции близка к масштабу сбоя - Опровергает: Если записей о коррекции часов нет, причина в другом. Для виртуальной машины проверить и случаи остановки с последующим возобновлением («Обслуживание облачного хоста и живая миграция») - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [ntpd - Network Time Protocol (NTP) daemon](https://www.ntp.org/documentation/4.2.8-series/ntpd/) · Network Time Foundation · Если расхождение превышает порог шага 128 ms, часы переводятся разом, если меньше, корректируются плавно. Скорость 0,5 ms в секунду, поэтому на 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 и диск. - Почему → Следствие → На экране: В заданное время запускаются задания ОС → Они делят CPU и диск с игровым сервером → В определённое время, например каждый день в 4:00, микрофризы и слоумо - Симптомы: Микрофризы, Слоумо / Факторы: Остановка - У кого: Весь сервер / Когда: С постоянным периодом - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Разнести задания по времени, понизить им приоритет (nice, ionice), отделить от игрового сервера (запускать на отдельном сервере). - На графике: Всплески с постоянным периодом (загрузка CPU, очередь диска, время тика сервера) - Где смотреть: Собрать время запуска плановых заданий из 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 получают доступ к диску, только когда им не пользуются другие программы - [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 дисковый ввод-вывод по процессам #### so-os-update · Изменение производительности после обновления ОС, ядра, драйверов или прошивки · Performance regression after OS / kernel / driver / firmware update Игровой код не менялся, но после обновления ОС, ядра, драйверов или прошивки сервер стал работать медленнее. Обновление может поменять значения по умолчанию, планировщик, защиту от уязвимостей CPU (mitigations) и поведение драйверов. - Почему → Следствие → На экране: Регулярный патч безопасности или новый образ сервера меняет ядро, драйверы или прошивку → Меняются значения по умолчанию или планировщик, включается новая защита от уязвимостей: на ту же работу уходит больше процессорного времени, а потоки получают CPU в другом порядке → Сервер, который работал нормально, со дня обновления постоянно чуть медленнее: задержка ввода, а при наплыве игроков микрофризы и слоумо - Симптомы: Задержка ввода, Микрофризы, Слоумо / Факторы: Задержка, Остановка, Джиттер - У кого: Весь сервер / Когда: Всегда, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Сначала обновлять часть серверов, сравнивать время тика, задержку и загрузку CPU с прежней версией и только потом раскатывать дальше. Не выкатывать в один день с игровым патчем. Записывать версии ядра, драйверов и прошивки и ключевые значения sysctl до и после обновления. При проблемах загрузиться со старым ядром и проверить. Отключение защиты (mitigations=off) решать, взвесив риски безопасности. - Цифры для ориентира: С новой версией ядра меняется и поведение по умолчанию. Например, начиная с 6.6 Linux стал переходить с планировщика CFS на EEVDF, а значение предела очереди подключений (somaxconn) по умолчанию с версии 5.4 выросло со 128 до 4 096. Защита от уязвимостей CPU добавляет работу: при возврате из ядра в программу (после каждого системного вызова), при переключении контекста и переключении виртуальных машин очищаются внутренние буферы CPU и т. п. Поэтому сильнее страдают сетевые серверы, которые делают системный вызов на каждый пакет. Для полной защиты от некоторых уязвимостей нужно выключить SMT (технологию, при которой одно ядро работает как два потока), а без SMT производительность в зависимости от нагрузки может сильно упасть. Параметр ядра mitigations=off отключает всю эту защиту и возвращает производительность, но оставляет систему уязвимой. - На графике: Ступенька вверх с определённого момента (время тика сервера, загрузка CPU, задержка при той же нагрузке) - Где смотреть: Сопоставить с моментом роста задержки историю обновлений в пакетном менеджере и время перезагрузки, версию ядра из uname -r и сведения о драйвере NIC из ethtool -i. Сравнить обновлённые и необновлённые серверы под одинаковой нагрузкой через mpstat и pidstat, а также состояние защиты в /sys/devices/system/cpu/vulnerabilities/ - Подтверждает: Задержка и загрузка CPU ступенькой поднимаются с момента перезагрузки после обновления и так и остаются, причём под одинаковой нагрузкой они выше только у обновлённых серверов. После загрузки со старым ядром или драйвером всё возвращается - Опровергает: Если обновлённые и необновлённые серверы под одинаковой нагрузкой одинаково медленные, причина в другом. Если в тот же день выкатили и игровой патч, а число или размер пакетов на игрока изменились, это «Патч изменил характер трафика» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Состояние защиты смотрят в файлах в /sys/devices/system/cpu/vulnerabilities/. По умолчанию (mitigations=auto) защита работает с включённым SMT, а с auto,nosmt на уязвимых CPU SMT выключается, и после обновления ядра число логических ядер может сократиться вдвое. Если обновить ОС в один день с игровым патчем, будет трудно понять, что стало причиной, поэтому их выкатывают отдельно. - Источники: - [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/. На многих 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 · Начиная с 6.6 Linux переходит с 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 Когда таблица отслеживания соединений (conntrack), в которую файрвол Linux записывает все соединения, достигает лимита, новые пакеты отбрасываются. - Почему → Следствие → На экране: Из-за наплыва подключений и множества коротких соединений растёт число записей → Таблица заполнена, новые соединения и часть пакетов отбрасываются → Ошибка входа, телепортация из-за необъяснимых потерь - Симптомы: Ошибка входа / бесконечная загрузка, Телепортация / Факторы: Потери - У кого: Весь сервер / Когда: Сразу после входа или техработ, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера, Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: сократить короткие соединения (для межсерверных вызовов переиспользовать соединения). Клиент: при неудачном подключении или обрыве увеличивать интервал между повторами и добавлять случайный разброс. - Команда инфраструктуры, задачи: Увеличить размер таблицы (nf_conntrack_max), исключить игровые порты из отслеживания (NOTRACK в таблице raw), настроить алерт по заполненности. - Цифры для ориентира: Лимит по умолчанию в зависимости от памяти сервера составляет примерно от 60 000 до 260 000 записей. При переполнении в журнале ядра появляется «nf_conntrack: table full, dropping packet». - На графике: Упор в лимит (плато) (число записей conntrack (nf_conntrack_count)) - Где смотреть: Вывести net.netfilter.nf_conntrack_count из sysctl (текущее число записей) на один график с nf_conntrack_max и найти в dmesg «nf_conntrack: table full, dropping packet» - Подтверждает: nf_conntrack_count выходит на плато на уровне max, и с этого момента в журнале ядра появляется table full - Опровергает: Если число записей далеко от max, причина в другом. Лимит отслеживания соединений самого инстанса AWS проверяют по conntrack_allowance_exceeded (см. «Превышен лимит PPS в облаке») - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 · Размер по умолчанию 65 536 при памяти больше 1 GB и 262 144 при памяти больше 4 GB (на 64-битных системах). При заполнении пакет отбрасывается с записью «nf_conntrack: table full, dropping packet» - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · CT --notrack в таблице raw исключает трафик из отслеживания соединений #### so-ports · Исчерпание эфемерных портов в межсерверных соединениях · Ephemeral port exhaustion (TIME_WAIT) Если игровой сервер часто открывает и закрывает короткие соединения с БД или другими серверами, закрытые соединения ещё какое-то время занимают порты, и новые соединения открыть не удаётся. - Почему → Следствие → На экране: На каждый запрос открывается и закрывается новое соединение → Сторона, закрывшая соединение первой, держит порт около 60 секунд (в Linux) в состоянии TIME_WAIT, и свободные порты заканчиваются → Внутренние запросы не проходят: сбои сохранения и ошибки функций - Симптомы: Съеденные действия / роллбэк, Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Весь сервер, Только одна функция / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Переиспользовать соединения (пул соединений), не открывать и не закрывать соединение на каждый запрос. - Команда инфраструктуры, задачи: Расширить диапазон портов (ip_local_port_range), рассмотреть повторное использование TIME_WAIT для исходящих соединений (tcp_tw_reuse в Linux), следить за числом TIME_WAIT. - Цифры для ориентира: Диапазон портов в Linux по умолчанию (32768–60999) даёт около 28 000 портов. Если открывать к одному и тому же адресу больше 470 новых соединений в секунду, порты заканчиваются. В Windows портов по умолчанию около 16 000 (49152–65535), а TIME_WAIT длиннее, поэтому порты заканчиваются ещё быстрее. - На графике: Упор в лимит (плато) (число сокетов в TIME_WAIT, число неудачных внутренних соединений) - Где смотреть: Подсчитать сокеты в TIME_WAIT по адресам назначения через ss -tan state time-wait и найти в логах игрового сервера ошибки connect (EADDRNOTAVAIL) - Подтверждает: Число TIME_WAIT к одному адресату (БД и т. п.) выходит на плато около размера диапазона эфемерных портов (по умолчанию около 28 000), а connect завершается с EADDRNOTAVAIL - Опровергает: Если TIME_WAIT мало, но сбоят только исходящие соединения во внешнюю сеть, это «Лимиты соединений и портов облачного NAT-шлюза» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: 60 секунд TIME_WAIT в Linux зашиты в ядро. Если уменьшить похожий по названию 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, закрытое соединение по умолчанию держит порт в TIME_WAIT 4 минуты - [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 · HOL-блокировка в TCP · Head-of-line blocking Чтобы сохранить порядок, TCP не передаёт игре пакеты, пришедшие позже, пока заново не получит один потерянный пакет. - Почему → Следствие → На экране: Один пакет теряется → Следующие пакеты уже пришли, но ждут в буфере приёма → Всё замирает, а потом разом прорывается: перемотка - Симптомы: Фриз, Перемотка / Факторы: Потери, Остановка - У кого: Только у меня / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: передавать позиции в реальном времени по UDP, гарантированную доставку оставить только для того, без чего нельзя, разделить данные на несколько потоков (streams). Клиент: перевести сетевую часть на ту же схему, что и сервер (UDP, раздельные каналы доставки). - Цифры для ориентира: Потеря одного пакета даёт остановку минимум на время пути туда и обратно плюс ещё немного, а если потеряется и повторная передача, остановка длится от сотен ms до нескольких секунд. - На графике: Провал, затем пачка (объём приёма по соединениям, число повторных передач) - Где смотреть: В захвате пакетов на стороне сервера (tcpdump, Wireshark) найти в соединении этого игрока повторно переданные пакеты и паузы вокруг них, а по серверу в целом смотреть прирост TcpRetransSegs в nstat -az - Подтверждает: Остановка начинается с повторной передачи одного пакета, а сразу после его прихода накопившиеся данные обрабатываются разом (приём держится на 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 · Потеря обнаруживается по 3 дублирующим ACK и запускается быстрая повторная передача, иначе приходится ждать таймер повторной передачи - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · Экспоненциальная задержка повтора (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: RetransSegs в группе Tcp (число повторно переданных сегментов) #### sk-rto · TCP RTO и экспоненциальный backoff · RTO and exponential backoff С каждой новой неудачной повторной передачей ожидание удваивается, и короткий обрыв связи превращается в долгую остановку. - Почему → Следствие → На экране: Связь ненадолго обрывается, и повторные передачи тоже одна за другой не доходят → Ожидание до следующей попытки каждый раз удваивается: 0,3 → 0,6 → 1,2 → 2,4 с (при пинге 100 ms) → Связь пропала на 1 с, а фриз в игре длится больше 2 с. Если обрыв дольше, в итоге дисконнект - Симптомы: Фриз, Дисконнект / Факторы: Потери, Остановка - У кого: Только у меня / Когда: Изредка, случайно, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: отвечать на хартбиты, а если их долго нет, первым закрывать соединение (сократить время до отказа через TCP_USER_TIMEOUT), восстанавливать сессию по токену, рассмотреть надёжный UDP. Клиент: часто отправлять хартбиты, а если ответы пропали, сразу переподключаться, не дожидаясь повторных передач TCP. - Цифры для ориентира: Минимальный RTO (время ожидания перед повторной передачей) в Linux равен «пинг + 200 ms», а при установке соединения он начинается с 1 секунды. С настройкой по умолчанию (tcp_retries2=15) повторные передачи могут раз за разом не проходить, но TCP отказывается от соединения только примерно через 15 минут. - На графике: Провал, затем пачка (RTO и backoff по соединениям, число срабатываний RTO) - Где смотреть: Посмотреть через ss -ti у зависшего соединения rto (ожидание перед повторной передачей в ms) и backoff (число срабатываний подряд), а по серверу в целом прирост TcpExtTCPTimeouts (число срабатываний таймера повторной передачи) в nstat -az - Подтверждает: У зависшего соединения backoff 1 и больше, rto вырос до секунд, и в этот момент растёт TCPTimeouts - Опровергает: Если всё решается быстрой повторной передачей и RTO не срабатывает, остановка короткая. Тогда это «HOL-блокировка в TCP» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · Начальный RTO 1 секунда, при каждом срабатывании таймера RTO удваивается (экспоненциальная задержка повтора) - [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 (200 ms), поэтому RTO не меньше RTT+200 ms - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_us по умолчанию 200 ms, начальный RTO для запроса на подключение 1 секунда, при tcp_retries2=15 до отказа проходит минимум 924,6 с (около 15 минут) - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rto (таймер повторной передачи, ms) и backoff (число экспоненциальных удвоений) в выводе -i - [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: TCPTimeouts в группе TcpExt - [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 растёт на единицу, а RTO удваивается (до максимального значения) #### sk-nagle · Алгоритм Нейгла + отложенный ACK · Nagle + delayed ACK (TCP_NODELAY off) Алгоритм Нейгла, который копит мелкие пакеты перед отправкой, и отложенный ACK, который придерживает подтверждения, мешают друг другу, и каждое сообщение, записанное по частям, задерживается на 40–200 ms. - Почему → Следствие → На экране: Мелкие сообщения пишутся по частям без включённого TCP_NODELAY → Отправитель ждёт ACK, а получатель отправляет ACK с задержкой → Пинг низкий, но все действия одинаково запаздывают: задержка ввода - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Весь сервер, Только у меня / Когда: Всегда, При определённом действии - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: включить TCP_NODELAY, собирать сообщения за тик и записывать их за один раз. На отключение отложенного ACK у получателя не полагаться (TCP_QUICKACK в Linux действует недолго, в Windows нужно править реестр на каждом ПК): игра не может надёжно этим управлять. Клиент: включить TCP_NODELAY, собирать сообщения за кадр и записывать их за один раз. - Цифры для ориентира: Отложенный ACK в Linux обычно 40 ms (в зависимости от ситуации до 200 ms). В старых версиях Windows было 200 ms, в современных 40 ms (в стандартном шаблоне Windows Server 2019 тоже 40 ms). Задержку ACK определяет ОС получателя, поэтому, если сервер с включённым Nagle отправляет сообщения по частям, задержка в зависимости от ПК игрока может составлять 40–200 ms. - На графике: Высоко с самого начала (время отклика на действие (RTT внутри игры)) - Где смотреть: Посмотреть в захвате пакетов на стороне сервера (tcpdump, Wireshark) интервалы между запросами и ответами и проверить, включает ли код сервера и клиента TCP_NODELAY - Подтверждает: Пинг низкий, но между мелкими пакетами повторяются паузы около 40 ms (в старых версиях Windows 200 ms), и каждая пауза заканчивается сразу после прихода ACK от другой стороны. С TCP_NODELAY паузы исчезают - Опровергает: Если интервал ответа близок к пингу, причина в другом. Если игровой сервер сам поздно формирует ответ, дело в обработке на сервере («Затор в очереди сообщений») - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [RFC 9293: Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293) · IETF · Пока есть данные без 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 · Отложенный ACK в Linux: минимум TCP_DELACK_MIN (HZ/25 = 40 ms), максимум TCP_DELACK_MAX (HZ/5 = 200 ms) - [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 · Таймаут отложенного ACK в Windows по умолчанию изменён на 40 ms (доклад 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 40 ms, MaxSynRetransmissions 2, InitialRto 3 000 ms - [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 · Старый TCP в Windows при получении данных запускал таймер отложенного ACK на 200 ms, а Nagle был включён по умолчанию, поэтому мелкие пакеты ждали ACK. Решение: TCP_NODELAY #### sk-block-send · Блокирующая отправка из-за медленного клиента · Blocking send on a full socket Если у одного игрока с медленным подключением заполнился буфер отправки, а сервер отправляет данные блокирующим способом (вызов не возвращается, пока в буфере не освободится место), поток сервера ждёт этого одного игрока. - Почему → Следствие → На экране: Буфер отправки медленного клиента заполнен → Отправка блокирующая, и поток сервера ждёт, пока в буфере освободится место → У всех, кого обслуживает этот поток, фриз или слоумо - Симптомы: Фриз, Слоумо / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: Изредка, случайно, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Перейти на неблокирующую отправку, ограничить очередь отправки для каждого клиента, выбрасывать устаревшие обновления. - На графике: Случайные всплески (время тика сервера, Send-Q по соединениям) - Где смотреть: Найти через ss -tn соединения, у которых Send-Q (байты без ACK или ещё не отправленные) заполнен на весь буфер отправки, и в момент всплеска времени тика посмотреть в дампе потоков (стеках) игрового сервера, нет ли потоков, застрявших в вызове send - Подтверждает: Когда есть медленное соединение с заполненным Send-Q, обслуживающий его поток стоит в send, и вместе с ним замирают только игроки, которых обслуживает тот же поток - Опровергает: Если остановившийся поток ждёт вне send (на блокировке, в вызове БД), это «Конкуренция за блокировки» или «Синхронные вызовы в игровом потоке» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 · Значения Recv-Q и Send-Q в ss: у слушающего сокета это число подключений, ждущих accept, и лимит backlog, у подключённого сокета это байты, ещё не прочитанные приложением, и отправленные байты без ACK #### sk-slow-client · Политика для медленных клиентов (slow consumer) · Slow-consumer policy Если для клиента постоянно копятся неотправленные данные, сервер выбрасывает устаревшие обновления или разрывает соединение. - Почему → Следствие → На экране: Подключение клиента не справляется с объёмом, который отправляет сервер → Сервер выбрасывает устаревшие обновления или при превышении лимита разрывает соединение → Только у этого игрока телепортация или дисконнект - Симптомы: Телепортация, Дисконнект / Факторы: Потери - У кого: Только у меня / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Сократить объём отправки (частота обновлений в зависимости от расстояния), продолжать отправку в пониженном качестве, уменьшить объём данных, который копится в ядре (TCP_NOTSENT_LOWAT в Linux). - Цифры для ориентира: При буфере отправки 256 KB и подключении 30 KB/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: максимум автоматически настраиваемого буфера отправки по умолчанию от 64 KB до 4 MB (в зависимости от памяти). 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 · Значения Recv-Q и Send-Q в ss: у слушающего сокета это число подключений, ждущих 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 с простоя, отправляет 9 проб с интервалом 75 с и, если ответа так и нет, разрывает соединение. Всего выходит около 2 ч 11 мин. Windows по умолчанию тоже начинает проверку только после 2 часов простоя. - На графике: Высоко только у некоторых (время с последнего приёма по соединениям) - Где смотреть: Посмотреть через ss -tnoi lastrcv (сколько ms прошло с последнего приёма) и таймер keepalive (timer:(keepalive,…)) по соединениям и сопоставить с записями игрового сервера об отказах «Аккаунт уже в игре» - Подтверждает: Остаются соединения ESTABLISHED с lastrcv от нескольких минут до нескольких часов, а перезаход с этого аккаунта отклоняется с ошибкой «Аккаунт уже в игре» - Опровергает: Если давно молчащих соединений нет, а «Аккаунт уже в игре» всё равно появляется, дело в коде очистки сессий на игровом сервере - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · После 7 200 с простоя 9 проб с интервалом 75 с (ещё около 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 · Таймаут TCP keepalive в Windows по умолчанию 2 часа - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · lastrcv в выводе -i (сколько ms прошло с последнего приёма), timer:(keepalive,…) в выводе -o #### sk-fragment · IP-фрагментация UDP-пакетов · IP fragmentation of large UDP UDP-пакет больше MTU (размера, который можно отправить за один раз) фрагментируется на уровне IP, и при потере всего одного фрагмента выбрасывается весь пакет. - Почему → Следствие → На экране: Снапшот людного места больше 1 500 байт → Пакет уходит несколькими фрагментами, и при потере любого из них выбрасывается целиком → Чем больше пакет, тем в разы выше доля потерь. Телепортация только в людных местах - Симптомы: Телепортация / Факторы: Потери - У кого: Одна локация или канал, Один регион или провайдер / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Самим разбивать данные на пакеты не больше 1 200 байт, отправлять только изменения. - Цифры для ориентира: На линии с потерями 2% пакет из 4 фрагментов теряется примерно в 8% случаев. Некоторые файрволы и провайдеры вообще отбрасывают фрагментированные пакеты, и тогда игрок не получает ни одного большого пакета. - На графике: Растёт вслед за онлайном и нагрузкой (число IP-фрагментов (IpFragCreates), размер снапшота) - Где смотреть: Посмотреть на сервере прирост IpFragCreates (число фрагментов, созданных при отправке) в nstat -az, а на принимающей стороне IpReasmFails (число неудачных сборок). Проверить распределение размеров UDP-пакетов по логам игрового сервера или захвату пакетов - Подтверждает: В людных местах растёт IpFragCreates, встречаются UDP-пакеты больше 1 500 байт, и в это же время растёт число жалоб на телепортацию - Опровергает: Если IpFragCreates не растёт, при отправке с сервера фрагментации нет - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Если потерян один фрагмент, пакет не собрать, и он теряется целиком. 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: FragCreates (число созданных фрагментов) и ReasmFails (число неудачных сборок) в группе Ip #### sk-reliable-udp · Настройка повторных передач в надёжном UDP · Reliable-UDP tuning (KCP, ENet…) Если собственные правила повторной передачи поверх UDP слишком осторожные, потери восстанавливаются поздно, а если слишком агрессивные, они ещё сильнее забивают линию. - Почему → Следствие → На экране: Интервал повторов, их число и размер окна не соответствуют качеству подключения → Восстановление запаздывает, либо дублирующие передачи усиливают перегрузку → Умения «съедаются», перемотка, при перегрузке лаги сильнее - Симптомы: Съеденные действия / роллбэк, Перемотка / Факторы: Потери, Задержка - У кого: Только у меня / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: повторно передавать по измеренному RTT, разделить каналы доставки по важности данных. Клиент: применить те же настройки повторной передачи и каналов, что и на сервере. - На графике: Случайные всплески (доля повторных передач надёжного UDP, RTT внутри игры) - Где смотреть: Логировать на сервере и клиенте статистику, которую используемая библиотека ведёт по каждому соединению (число повторных передач, оценка RTT, ожидание перед повтором), и сравнить с реальной долей потерь на подключении того же игрока (измеренной mtr) - Подтверждает: Если доля повторных передач в разы выше реальной доли потерь, настройки слишком агрессивные. Если ожидание перед повтором в разы больше измеренного RTT, слишком осторожные - Опровергает: Если доля повторных передач близка к доле потерь, а ожидание соответствует RTT, с настройками всё в порядке. Смотреть сами потери на подключении - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Повторные передачи могут усиливать перегрузку, поэтому на них распространяется управление перегрузкой. RTT оценивают как среднее по нескольким измерениям (EWMA), начальное значение 1 секунда, при срабатывании таймера скорость передачи снижают #### sk-slowstart · Медленный старт после простоя · Slow start after idle Если соединение какое-то время простаивало, TCP снова уменьшает окно перегрузки (объём, который можно отправить за раз), и внезапно понадобившийся большой объём данных уходит в несколько заходов. - Почему → Следствие → На экране: По простаивавшему соединению отправляется большой объём данных, например при входе в город → Окно перегрузки уменьшено, и передача растягивается на несколько RTT → Сразу после входа окружающие персонажи и NPC появляются с опозданием на несколько RTT (тем заметнее, чем дальше сервер) - Симптомы: Задержка ввода, Невидимки / фантомы / Факторы: Задержка - У кого: Только у меня / Когда: В движении и при смене локации, После бездействия - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Уменьшить объём данных при входе (сначала отправлять самое необходимое). - Команда инфраструктуры, задачи: Выключить tcp_slow_start_after_idle (Linux, настройка на весь сервер). - Цифры для ориентира: Если простой дольше RTO, окно перегрузки начинает уменьшаться и после долгого простоя опускается примерно до 14 KB (10 пакетов). Тогда 100 KB не уйдут за один раз и будут переданы за 3 RTT. - На графике: Высоко только у некоторых (время передачи сразу после входа (у игроков с большим RTT)) - Где смотреть: Проверить значение sysctl net.ipv4.tcp_slow_start_after_idle и в момент входа в локацию после простоя посмотреть в ss -ti, не уменьшился ли cwnd (окно перегрузки) этого соединения - Подтверждает: Параметр равен 1 (по умолчанию), в момент входа после простоя cwnd падает примерно до 10, и передача растягивается на несколько RTT. Чем больше 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 · cwnd (окно перегрузки) и ssthresh (порог медленного старта) в выводе -i #### sk-congestion · Резкий спад передачи из-за управления перегрузкой · Congestion control backoff TCP считает потери признаком перегрузки и снижает скорость передачи на 30–50%. Точно так же он реагирует и на потери в Wi-Fi. - Почему → Следствие → На экране: Когда отправлять нужно много, в Wi-Fi или на линии случаются небольшие потери → TCP резко снижает скорость передачи и восстанавливает её медленно (CUBIC, алгоритм по умолчанию в Linux и Windows, снижает на 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 · cwnd, ssthresh и название алгоритма управления перегрузкой в выводе -i - [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 · Значения Recv-Q и Send-Q в ss: у слушающего сокета это число подключений, ждущих accept, и лимит backlog, у подключённого сокета это байты, ещё не прочитанные приложением, и отправленные байты без ACK #### sk-linger · Потеря последних данных при принудительном закрытии через RST · SO_LINGER, abrupt RST Если сервер резко рвёт соединение, последнее отправленное сообщение или подтверждение сохранения пропадает. - Почему → Следствие → На экране: Сервер закрывает соединение принудительно (RST). Так бывает, если SO_LINGER выставлен в 0 с или сокет закрыт, когда полученные данные ещё не дочитаны → Причина кика и последние данные, которые ещё передавались, выбрасываются → Сообщение «Соединение закрыто из-за неизвестной ошибки» без видимой причины - Симптомы: Дисконнект / Факторы: Потери - У кого: Только у меня / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Отправив причину, сначала закрыть только направление отправки (shutdown), дочитать полученные данные, пока другая сторона не закроет соединение, и только потом закрыть сокет. Не использовать SO_LINGER с 0 с. - На графике: Случайные всплески (число соединений, завершённых через RST) - Где смотреть: Посмотреть прирост TcpExtTCPAbortOnData (закрыто через RST с неотправленными данными, SO_LINGER 0 с) и TcpExtTCPAbortOnClose (закрыто с непрочитанными данными) в nstat -az, а в захвате пакетов на стороне сервера проверить, уходит ли в момент обрыва RST вместо FIN - Подтверждает: В моменты жалоб на «Соединение закрыто из-за неизвестной ошибки» сервер отправляет 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: соединение закрыто через RST с неотправленными данными (например, при SO_LINGER 0 с). TcpExtTCPAbortOnClose: сокет закрыт с непрочитанными данными, и отправлен RST #### sk-blocking-io · Архитектура с блокирующим вводом-выводом · Blocking I/O model Если поток, ожидая один сокет, не может делать ничего другого, то с ростом числа игроков тормозит всё. - Почему → Следствие → На экране: Поток ждёт чтения и записи по каждому соединению → Задержка одного соединения распространяется на другие соединения того же потока → Чем выше онлайн, тем сильнее у всех слоумо и задержка ввода - Симптомы: Слоумо, Задержка ввода / Факторы: Остановка - У кого: Весь сервер / Когда: При наплыве игроков, Вечерний пик - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Перейти на асинхронный ввод-вывод на базе epoll, IOCP или io_uring. - На графике: Растёт вслед за онлайном и нагрузкой (время ответа, число потоков) - Где смотреть: Посмотреть через 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 Completion Ports](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports) · Microsoft · Способ Windows обрабатывать множество асинхронных операций ввода-вывода заранее созданным пулом потоков - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · cswch/s в выводе -w: добровольные переключения контекста, когда поток сам останавливается в ожидании ресурса. -t выводит данные по потокам #### sk-reuseport · Перекос распределения в SO_REUSEPORT · SO_REUSEPORT imbalance, stuck worker Когда несколько процессов принимают трафик на одном порту, ядро закрепляет каждое подключение за процессом по хешу адреса и больше его не меняет. Если один процесс зависает, ждут только игроки, закреплённые за ним. - Почему → Следствие → На экране: Шлюз или сервер авторизации запускает несколько процессов с SO_REUSEPORT → Если один процесс останавливается из-за GC или перегрузки, закреплённые за ним новые подключения и UDP-пакеты к другим процессам не переходят → Только у части игроков ошибка входа или фриз. При перезапуске со сменой числа процессов часть UDP-сессий обрывается - Симптомы: Ошибка входа / бесконечная загрузка, Фриз, Дисконнект / Факторы: Остановка, Потери - У кого: Только у меня, Весь сервер / Когда: Сразу после входа или техработ, Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Не допускать остановок принимающего потока, реализовать передачу сессий при перезапуске. - Команда инфраструктуры, задачи: Следить за очередью подключений каждого процесса (Recv-Q в ss), а если при деплое меняется число процессов, следовать процедуре передачи сессий. - На графике: Высоко только у некоторых (очередь подключений (Recv-Q) по слушающим сокетам) - Где смотреть: Посмотреть через ss -ltnp для каждого слушающего сокета на этом порту Recv-Q (число подключений, ждущих accept) и процесс-владелец, сравнить пропускную способность процессов - Подтверждает: Из нескольких сокетов на одном порту 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 делит подключения по очередям воркеров простым хешем, поэтому если один воркер застрял, встают все подключения в его очереди - [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 · Значения Recv-Q и Send-Q в ss: у слушающего сокета это число подключений, ждущих accept, и лимит backlog, у подключённого сокета это байты, ещё не прочитанные приложением, и отправленные байты без ACK #### sk-udp-connreset · Ошибка WSAECONNRESET на UDP-сокете в Windows · WSAECONNRESET on a Windows UDP socket Когда сервер на Windows отправляет UDP уже ушедшему клиенту, обратно приходит уведомление «порт недоступен» (ICMP). Из-за него следующий вызов приёма завершается ошибкой, и если код сервера считает её поломкой самого сокета, страдают все, кто работает через этот сокет. - Почему → Следствие → На экране: Сервер продолжает слать UDP на адрес только что вышедшего клиента, и обратно приходит «порт недоступен» (ICMP) → Windows завершает следующий вызов приёма ошибкой WSAECONNRESET (10054), а код сервера прекращает приём или закрывает сокет → У всех, кто работал через этот сокет, разом фриз или дисконнект - Симптомы: Дисконнект, Фриз / Факторы: Потери, Остановка - У кого: Весь сервер / Когда: Изредка, случайно, Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Отключить SIO_UDP_CONNRESET через WSAIoctl, чтобы не получать это уведомление, а при ошибке приёма только писать в лог и продолжать приём. - На графике: Массовый обрыв соединений (число подключений, лог ошибок приёма) - Где смотреть: Найти в логах игрового сервера коды ошибок приёма 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 · WSAECONNRESET на UDP-сокете означает, что на предыдущую отправку пришёл ответ 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 Если работа одного тика не укладывается в бюджет, интервал тиков сервера растягивается, и вся локация идёт замедленно или с микрофризами. - Почему → Следствие → На экране: Работа на один тик (например, 50 ms) не укладывается в бюджет → Состояние игры, которое нужно считать 20 раз в секунду, считается только 8 раз → Слоумо во всей локации (в зависимости от архитектуры сервера микрофризы), умения срабатывают с опозданием - Симптомы: Слоумо, Задержка ввода, Микрофризы / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: При наплыве игроков, Вечерний пик - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Сократить дорогие расчёты, распределить тик по нескольким потокам, распределять игроков (по каналам), записывать время обработки тика в метрики. - Команда инфраструктуры, задачи: Добавить время тика и загрузку CPU по ядрам в мониторинг и алерты, рассмотреть CPU и инстансы с высокой однопоточной производительностью (частотой). - Цифры для ориентира: У 20-тикового сервера бюджет 50 ms, у 30-тикового 33 ms, у 60-тикового 16,7 ms. На случай внезапного наплыва безопаснее держать запас и в обычное время расходовать примерно половину бюджета. - На графике: Растёт вслед за онлайном и нагрузкой (время тика сервера, число игроков по зонам и каналам, CPU игрового потока) - Где смотреть: Вывести на один график время обработки тика (p99) и число превышений бюджета тика из метрик сервера вместе с числом игроков по зонам и каналам. Если метрик тика нет, смотреть загрузку CPU игрового потока через pidstat -t 1 - Подтверждает: В моменты наплыва игроков время тика превышает бюджет (50 ms для 20-тикового сервера), а загрузка CPU игрового потока всё это время держится около 100% - Опровергает: Если тик не укладывается в бюджет, а CPU игрового потока загружен слабо, причина в ожидании (пауза GC, блокировки, синхронные вызовы). Если bcc runqlat показывает долгое ожидание в очереди выполнения, потоку не достаётся CPU: дело в нехватке CPU или избытке потоков - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Как выглядят опаздывающие тики, зависит от архитектуры сервера. Если сервер за каждый тик продвигает состояние игры на фиксированное время (например, 50 ms), замедляется само игровое время, и получается слоумо. Если сервер за раз продвигает игру на реально прошедшее время, скорость игры сохраняется, но пакеты приходят реже, а объекты сдвигаются рывками, и это выглядит как микрофризы и телепортация. В обоих случаях отклик на ввод запаздывает. Если весь сервер обслуживает один игровой поток, тормозит весь сервер, а если у каждой локации свой поток, тормозит эта локация. Некоторые игры, например 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-тиковый сервер должен укладываться в 7,8125 ms на кадр. Время серверного кадра измеряют по подсистемам и делят бюджет между ними - [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 раз. - Почему → Следствие → На экране: Расстояние сравнивается между всеми персонажами, или карта разбита на сетку, но вокруг одной ячейки собираются сотни игроков → Для 100 игроков около 10 000 сравнений, для 1 000 около 1 000 000 → Там, где все собрались вместе (мировой босс, осада), время тика взлетает: слоумо, микрофризы - Симптомы: Слоумо, Микрофризы / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Разбить мир на сетку или области и сравнивать только соседей, реже обновлять дальние объекты, ограничить число игроков, которых видит один игрок. - Цифры для ориентира: Если на каждую пару игроков с учётом сравнения расстояния и обновления списков видимости заложить 0,1 µs (одна десятимиллионная секунды), то для 1 000 игроков (около 1 000 000 пар) это 100 ms на тик. Вдвое больше бюджета 20-тикового сервера (50 ms). - На графике: Растёт вслед за онлайном и нагрузкой (время тика сервера, число игроков в одном месте) - Где смотреть: Вывести на один график число игроков по зонам и каналам и время тика, а также отдельно измеренное время расчёта видимости внутри тика. Если отдельного замера нет, смотреть долю CPU по функциям игрового процесса через perf top -p - Подтверждает: Когда игроков в одном месте становится вдвое больше, время тика растёт почти в 4 раза, а большую часть процессорного времени занимают функции расчёта видимости и расстояний - Опровергает: Если время тика растёт пропорционально числу игроков или велика доля функций отправки и сериализации, дело во взрывном росте рассылки или в затратах на сериализацию и сжатие - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [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 · В реальном времени показывает долю CPU работающего процесса (-p) или потока (-t) по функциям (символам) #### sp-broadcast · Взрывной рост рассылки (broadcast) · Broadcast fan-out (N×N) Если движение каждого игрока отправлять всем, кто его видит, число обновлений растёт как квадрат числа собравшихся. - Почему → Следствие → На экране: Изменение у одного игрока рассылается всем, кто его видит → Если 1 000 игроков видят друг друга, это 1 000 000 обновлений за тик → Очереди отправки и пропускная способность забиты: задержки и потери (задержка ввода, перемотка, телепортация) - Симптомы: Задержка ввода, Телепортация, Перемотка / Факторы: Задержка, Потери - У кого: Одна локация или канал, Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Снижать частоту обновлений по расстоянию и важности (близкие враги каждый тик, дальние игроки несколько раз в секунду), ограничить объём отправки одному игроку и заполнять его в порядке важности, объединять несколько обновлений в один пакет, ограничить число отображаемых игроков. - Команда инфраструктуры, задачи: Сравнивать исходящую пропускную способность и число пакетов в секунду на каждом сервере с сетевыми лимитами NIC и инстанса и настроить алерты, проверять запас перед крупными событиями. - Цифры для ориентира: 1 000 игроков × 1 000 игроков × 20 тиков = 20 000 000 обновлений в секунду. При 40 байтах на обновление это около 6,4 Gbps на весь сервер и около 6,4 Mbps на каждого получателя. Если ограничить число видимых игроков до 150, выходит около 1 Gbps на сервер и около 1 Mbps на игрока. - На графике: Растёт вслед за онлайном и нагрузкой (исходящие пакеты и байты сервера, число игроков в одном месте) - Где смотреть: Смотреть txpck/s и txkB/s из sar -n DEV 1 (пакеты и KB, отправленные NIC сервера за секунду) вместе с графиком числа игроков. Для облачных инстансов смотреть счётчики превышения лимитов в ethtool -S (для AWS ENA bw_out_allowance_exceeded и pps_allowance_exceeded) - Подтверждает: С ростом числа собравшихся исходящие пакеты и байты растут круче, чем число игроков (почти квадратично), а с момента упора в лимит растут счётчики превышения лимитов или отбрасывания при отправке - Опровергает: Если исходящий трафик не меняется, а растёт только время тика, дело в расчёте видимости или игровой логике - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Реальные инциденты: eve-hedgp-2014 - Источники: - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · Рассылка O(n²), когда действия n игроков должны увидеть 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 · rxpck/s и txpck/s в sar -n DEV (принятые и отправленные пакеты в секунду), 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 Если каждую локацию обслуживает один поток, то при скоплении игроков в одном месте на 100% загружается только одно ядро. - Почему → Следствие → На экране: Одну локацию (канал) обслуживает один поток → Когда игроки собираются в одном месте, загружено только это ядро, остальные свободны → Лагает только эта локация, в других всё нормально - Симптомы: Слоумо, Задержка ввода / Факторы: Остановка - У кого: Одна локация или канал / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Распределять игроков по каналам, распараллелить обработку внутри локации, ограничить число игроков. - Команда инфраструктуры, задачи: Добавить загрузку CPU по ядрам в мониторинг и алерты (в среднем по серверу перегрузка одного ядра теряется). - Цифры для ориентира: На 16-ядерном сервере одно ядро на 100% даёт общую загрузку CPU всего около 6%. Найти это можно, только глядя на загрузку по ядрам. - На графике: Растёт вслед за онлайном и нагрузкой (загрузка CPU по ядрам, CPU по потокам) - Где смотреть: Посмотреть загрузку по ядрам через mpstat -P ALL 1 и CPU по потокам игрового процесса через pidstat -t 1, сравнить с числом игроков в зоне, которую обслуживает самый загруженный поток - Подтверждает: Общая загрузка CPU низкая, но один поток (одно ядро) держится около 100%, и в это время в зоне этого потока скопились игроки - Опровергает: Если равномерно загружены многие ядра, это общая перегрузка сервера. Если высок только %soft (обработка приёма) на одном ядре, дело в том, что прерывания NIC сосредоточены на одном ядре - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Другие локации в порядке, только если тики в каждой локации идут независимо. Если потоки локаций каждый тик ждут друг друга, чтобы вместе перейти к следующему тику, самая загруженная локация замедляет тики всего сервера. - Реальные инциденты: eve-hedgp-2014 - Источники: - [Time Dilation – How’s That Going?](https://www.eveonline.com/news/view/time-dilation-hows-that-going) · CCP Games · Time Dilation в EVE Online действует на весь узел, поэтому замедляются и далёкие звёздные системы на том же узле. Крупные сражения обрабатывают на усиленных узлах, где размещены только 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 Если несколько потоков ждут одну блокировку, чтобы работать с одними и теми же данными, то сколько потоков ни добавляй, выполняется только один за раз. - Почему → Следствие → На экране: Несколько потоков одновременно работают с общими данными, например аукционом или хранилищем гильдии → Пока поток, захвативший блокировку, не закончит, остальные ждут → Тормозит только определённая функция, в тяжёлых случаях отстают тики всего сервера - Симптомы: Задержка ввода, Фриз / Факторы: Остановка - У кого: Только одна функция, Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Дробить блокировки, сокращать работу под блокировкой, перейти на архитектуру на сообщениях (у каждых данных свой поток-владелец, а остальные потоки только отправляют ему запросы сообщениями). - Цифры для ориентира: Если работа под блокировкой занимает 20% задачи, то сколько потоков ни добавляй, производительность вырастет максимум в 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 загружен полностью, дело в объёме расчётов (превышение бюджета тика, перегрузка однопоточной локации). Если потоки ждут в вызовах БД или файлов, дело в синхронных вызовах в игровом потоке - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Это бывает там, где несколько потоков вместе меняют игровые данные. Если каждую локацию или функцию обслуживает один поток и потоки общаются только сообщениями, блокировок почти нет, зато нужно следить, чтобы работа не скапливалась на одном потоке (перегрузка однопоточной локации). Если игровой поток ждёт блокировку, которую держит медленная операция сохранения, стоит весь тик. - Реальные инциденты: 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 работают по однопоточной модели и обрабатывают запросы по одному до конца, поэтому состояние не меняется одновременно. Если грейны ждут ответа друг от друга, возможен дедлок - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · cswch/s в выводе -w: число добровольных переключений контекста, когда поток остановился в ожидании ресурса. -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 Если два потока ждут блокировки, захваченные друг другом, они встают навсегда. - Почему → Следствие → На экране: Поток A держит блокировку 1 и ждёт блокировку 2, а поток B держит блокировку 2 и ждёт блокировку 1 → Оба встают навсегда, а за ними цепочкой встают и связанные потоки → Весь сервер стоит, watchdog его перезапускает, у всех дисконнект - Симптомы: Фриз, Дисконнект / Факторы: Остановка - У кого: Весь сервер, Только одна функция / Когда: Изредка, случайно, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Ввести правило порядка захвата блокировок, использовать блокировки с таймаутом, поставить watchdog и сохранять дамп потоков в момент зависания. - На графике: Массовый обрыв соединений (число подключений, исходящий трафик сервера) - Где смотреть: Пока сервер стоит, снять стеки вызовов всех потоков. Для JVM jstack (сам находит и показывает дедлоки), для .NET dotnet-stack, для нативного сервера thread apply all bt в gdb, либо снять core-файл через gcore и разобрать его после перезапуска - Подтверждает: Два или больше потоков стоят со стеками, в которых каждый ждёт блокировку, захваченную другим, а загрузка CPU процессом всё это время близка к 0 - Опровергает: Если во время зависания один поток крутится на 100% CPU, это бесконечный цикл. Если потоки ждут ответа БД или внешних сервисов, дело в синхронных вызовах или исчерпании пула потоков - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [Runtime locking correctness validator](https://docs.kernel.org/locking/lockdep-design.html) · Linux kernel · Если две блокировки захватываются в противоположном порядке, возникает циклическое ожидание и дедлок (lock inversion deadlock). Ядро Linux проверяет порядок захвата блокировок и предупреждает заранее - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · Дедлок, при котором приложение работает, но не может продвинуться, ловят liveness-проверкой и перезапускают контейнер - [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 · Создаёт core-файл работающей программы, и после этого программа продолжает работать #### sp-sync-call · Синхронные вызовы в игровом потоке · Synchronous DB / file I/O on the game loop Если посреди тика ждать ответа БД или записи в файл, на это время останавливается вся игра на сервере. - Почему → Следствие → На экране: Внутри тика поток ждёт запросов и сохранений в БД, записи логов, вызовов внешних API → Если БД отвечает 100 ms, тик тоже стоит 100 ms → Каждый раз, когда тормозит БД или диск, вся открытая локация на миг замирает - Симптомы: Фриз, Микрофризы / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: При определённом действии, Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Всю медленную работу (запросы и сохранения в БД, запись логов, вызовы внешних API) выполнять асинхронно, а результат применять в следующем тике (если только поставить таймаут, на время ожидания игра всё равно встанет). - Цифры для ориентира: Даже RTT до БД в том же ЦОД 0,5 ms при 100 вызовах за тик даёт 50 ms. Это весь бюджет 20-тикового сервера. - На графике: Случайные всплески (время тика сервера, задержка запросов к БД) - Где смотреть: Наложить на одну временную ось график времени тика, задержку запросов к БД (slow query log и т. п.) и задержку диска. Если метрик тика нет, смотреть через bcc offcputime -p, где ждёт игровой поток - Подтверждает: Всплески времени тика совпадают со всплесками задержки БД или файлов, а ожидание игрового потока приходится на стеки вызовов приёма ответа БД и записи в файл - Опровергает: Если задержки БД и диска ровные, а время тика скачет, дело в паузах 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,5 ms (500 000 ns) - [ASP.NET Core Best Practices](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/best-practices) · Microsoft · Доступ к данным, ввод-вывод и долгие операции вызывать асинхронно. Синхронные блокирующие вызовы ведут к исчерпанию пула потоков и задержкам ответа - [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 Если респавн всех монстров, окончание всех баффов и награды ровно в начале часа приходятся на один тик, этот тик становится в десятки раз тяжелее. - Почему → Следствие → На экране: Таймеры респавна, окончания эффектов, наград и автосохранения выставлены на одно и то же время → В этот один тик работы в десятки раз больше обычного → В определённое время каждый раз всё на миг замирает - Симптомы: Фриз, Микрофризы / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: С постоянным периодом - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Немного разбрасывать время таймеров случайным образом, распределять обработку по нескольким тикам. - На графике: Всплески с постоянным периодом (время тика сервера) - Где смотреть: Собрать моменты всплесков времени тика и посмотреть на интервалы (ровно в начале часа, каждые 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. Ко всем таймерам, периодическим и отложенным заданиям добавляют случайный разброс (jitter), чтобы рассеять нагрузку, которая скапливается в одно время. Пример: ежеминутные запросы с многих серверов скапливались в первые секунды каждой минуты #### sp-pathfinding · Лавина поиска пути · Pathfinding storms Когда сотни монстров одновременно гонятся за игроками и рассчитывают путь, это сильно нагружает CPU. - Почему → Следствие → На экране: Из-за сбора монстров «паровозом» и массового спавна монстры одновременно преследуют игроков → Каждый монстр рассчитывает путь → Слоумо только на этом месте фарма - Симптомы: Слоумо / Факторы: Остановка - У кого: Одна локация или канал / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Кэшировать пути, ограничить число расчётов, распределять их по нескольким тикам. - На графике: Растёт вслед за онлайном и нагрузкой (время тика сервера, число монстров по зонам) - Где смотреть: Смотреть число монстров, преследующих игроков, по зонам и время тика. Если отдельного подсчёта нет, смотреть долю CPU по функциям игрового процесса через perf top -p - Подтверждает: При сборе «паровозом» и массовом спавне время тика растёт, а функции поиска пути занимают большую долю процессорного времени - Опровергает: Если время тика растёт, когда монстров мало, а игроков много, дело в расчёте видимости или рассылке - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [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 · В реальном времени показывает долю CPU работающего процесса (-p) по функциям (символам) #### sp-serialize · Затраты на сериализацию и сжатие · Serialization / compression cost Превращение данных для отправки в байты и их сжатие тоже требуют CPU, и при большом числе игроков эти затраты резко растут. - Почему → Следствие → На экране: Для каждого обновления структура преобразуется в байты и сжимается → Затраты растут пропорционально квадрату числа игроков → Отправка запаздывает: задержка ввода - Симптомы: Задержка ввода / Факторы: Остановка, Задержка - У кого: Одна локация или канал / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Переиспользовать один собранный пакет для многих получателей, использовать лёгкий формат. - На графике: Растёт вслед за онлайном и нагрузкой (загрузка CPU сервера, CPU потока, собирающего пакеты) - Где смотреть: Сравнить через perf top -p долю функций сериализации, сжатия и шифрования (включая функции библиотек вроде zlib, LZ4, OpenSSL) в процессорном времени игрового процесса при малом числе игроков и при наплыве - Подтверждает: Чем больше игроков, тем выше доля функций сериализации, сжатия и шифрования, и первым упирается в CPU поток, собирающий пакеты - Опровергает: Если доля этих функций мала, дело в расчёте видимости или игровой логике - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Если соединение шифруется (TLS, DTLS и т. п.), CPU тратится ещё и на шифрование и расшифровку. Шифрование делается отдельно для каждого соединения, поэтому, даже если один собранный пакет переиспользуется для многих получателей, шифровать его приходится столько раз, сколько получателей. Симметричные шифры вроде AES-GCM настолько быстры, что одно ядро обрабатывает несколько GB в секунду, и обычно их доля невелика. Но скорость сильно зависит от размера единицы шифрования (записи), и когда, как в играх, много мелких пакетов, затраты на байт растут. При рукопожатии, которое выполняется один раз на подключение, сервер подписывает данные ключом сертификата и вычисляет обмен ключами (ECDHE). Одно ядро успевает в секунду от примерно 1 100 (RSA 2048) до 18 000 (ECDSA P-256) подписей и около 9 000 обменов ключами, поэтому при наплыве входов это заметная нагрузка. - Источники: - [Introduction to Iris in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/introduction-to-iris-in-unreal-engine) · Epic Games · Реплицируемое состояние хранится в одной квантованной копии, что сокращает дорогую работу, а её результат используют сразу несколько подключений - [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,7 GB в секунду (сильно зависит от размера записи). Одно ядро в секунду выполняет 1 120 подписей RSA 2048, 18 477 подписей ECDSA P-256 и 9 394 обмена P-256 ECDHE. На edge-серверах Cloudflare TLS-библиотека расходует около 1,8% CPU - [perf-top(1) — Linux manual page](https://man7.org/linux/man-pages/man1/perf-top.1.html) · perf · В реальном времени показывает долю CPU работающего процесса (-p) по функциям (символам) #### sp-crash · Падение сервера · Server process crash Если процесс сервера падает из-за необработанной ошибки, у всех, кто был на этом сервере, одновременно обрывается соединение. - Почему → Следствие → На экране: Фатальная ошибка: обращение к несуществующему объекту (нулевая ссылка), некорректные данные, нехватка памяти и т. п. → Процесс сервера (или зоны) завершается → Дисконнект у всех одновременно, прогресс после последнего сохранения может уйти в роллбэк - Симптомы: Дисконнект, Съеденные действия / роллбэк / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: Изредка, случайно, При определённом действии - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Разбирать краш-дампы и исправлять причины, сохранять чаще. - Команда инфраструктуры, задачи: Настроить автоматический перезапуск процесса, сбор и хранение краш-дампов, мгновенный алерт при падении сервера. - На графике: Массовый обрыв соединений (число подключений, число перезапусков процесса) - Где смотреть: Смотреть записи о core dump в coredumpctl list (время, PID, сигнал завершения) и записи менеджера сервисов (systemd) об аварийных завершениях и перезапусках. На серверах Windows смотреть дампы, сохранённые WER - Подтверждает: В момент, когда число подключений резко падает почти до 0, есть аварийное завершение процесса игрового сервера и core dump - Опровергает: Если процесс жив, а соединения оборвались, дело в сетевом оборудовании или таймауте простоя. Если есть запись о перезапуске watchdog после долгого зависания, дело в бесконечном цикле или дедлоке - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 автоматически перезапускает сервис при аварийном завершении, завершении по сигналу (в том числе с core dump) и срабатывании watchdog. Рекомендуется для долгоживущих сервисов - [coredumpctl(1) — Linux manual page](https://man7.org/linux/man-pages/man1/coredumpctl.1.html) · systemd · list показывает core dump, сохранённые systemd-coredump: время падения, PID и сигнал, вызвавший падение #### sp-threadpool · Исчерпание пула потоков · Thread pool starvation Если все рабочие потоки заняты медленными задачами, новые запросы ждут неопределённо долго. - Почему → Следствие → На экране: Рабочие потоки заняты ожиданием ответа внешних API или БД → Для новых запросов не остаётся свободных потоков → Бесконечная загрузка в отдельных функциях, например при входе или в магазине - Симптомы: Ошибка входа / бесконечная загрузка, Задержка ввода, Фриз / Факторы: Остановка - У кого: Только одна функция, Весь сервер / Когда: При наплыве игроков, Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Поставить таймауты на медленные вызовы, разделить пулы потоков по функциям, перейти на асинхронные вызовы. - На графике: Упор в лимит (плато) (число потоков и длина очереди пула потоков, время обработки запросов) - Где смотреть: Для .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%, а число потоков медленно, но постоянно растёт или держится на пределе, очередь копится, и большинство рабочих потоков ждут ответа на один и тот же внешний вызов (БД, 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 запросах в секунду рост задержки со 100 ms до 10 с увеличивает нужное число потоков с 10 до 1 000, и пул исчерпывается - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · Microsoft Azure · Если для каждого вызываемого сервиса держать отдельный пул соединений и потоков, сбой одного сервиса блокирует только его пул - [.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 · ThreadPool Thread Count (threadpool-thread-count) и ThreadPool Queue Length (threadpool-queue-length) в .NET 8 и ниже #### sp-infinite-loop · Бесконечный цикл и неконтролируемая логика · Infinite loop / runaway logic Если из-за бага тик не заканчивается, сервер встаёт, и watchdog принудительно его перезапускает. - Почему → Следствие → На экране: Из-за ошибочного условия цикл не завершается, или рекурсия уходит вразнос → Тик не заканчивается, сервер стоит → Фриз, затем дисконнект у всех - Симптомы: Фриз, Дисконнект / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: При определённом действии, Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Ограничить число итераций, поставить watchdog, написать тесты, воспроизводящие проблемный ввод. - На графике: Массовый обрыв соединений (число подключений, CPU по потокам) - Где смотреть: Во время зависания посмотреть CPU по потокам через pidstat -t 1 и проверить через perf top -t (ID потока) или gdb, в какой функции крутится поток на 100%. Если сервер уже перезапущен, искать записи о срабатывании watchdog (WatchdogSec в systemd, проваленная liveness-проверка в Kubernetes) - Подтверждает: Пока сервер стоит, один игровой поток держится на 100% CPU, а его стек всё время крутится внутри одной функции или цикла - Опровергает: Если во время зависания загрузка 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-проверкой и перезапускают. По умолчанию проверка идёт каждые 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 · В реальном времени показывает долю CPU работающего потока (-t) или процесса (-p) по функциям (символам) #### sp-hot-entity · Бой, сосредоточенный на одной цели (мировой босс) · Hot entity / combat event fan-out Когда сотни игроков одновременно бьют одного босса, все расчёты по этому боссу сходятся в одной точке, а информация о каждом ударе рассылается всем, кто его видит. - Почему → Следствие → На экране: Сотни игроков без остановки применяют к одному боссу умения, баффы и дебаффы → Расчёты здоровья босса, списка агро и дебаффов сходятся в одной точке, а пакеты с цифрами урона и эффектами за каждый удар рассылаются всем, кто это видит → Умения проходят с опозданием, цифры урона вылетают пачкой, слоумо только вокруг босса - Симптомы: Задержка ввода, Перемотка, Слоумо / Факторы: Остановка, Задержка - У кого: Одна локация или канал / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Объединять или не показывать чужие цифры урона и эффекты, ограничить число дебаффов на одной цели, распределять обработку ударов по нескольким тикам. - Цифры для ориентира: Если 800 игроков бьют по 2 раза в секунду, это 1 600 ударов в секунду. Если сообщать о каждом всем 800 зрителям, выходит 1 280 000 сообщений в секунду. - На графике: Растёт вслед за онлайном и нагрузкой (время тика сервера, число отправленных сообщений) - Где смотреть: Смотреть время тика и число отправленных пакетов во время боя с боссом вместе с числом игроков вокруг босса, а если есть возможность, и число событий (ударов, баффов, дебаффов) в секунду по целям - Подтверждает: С ростом числа игроков вокруг босса время тика и исходящий трафик круто растут, а событий в секунду у одного босса в десятки раз больше, чем у других целей - Опровергает: Если всё так же тормозит при любом скоплении игроков, независимо от босса, дело в расчёте видимости или взрывном росте рассылки - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · Даже об одной атаке нужно сообщить всем видящим её клиентам, поэтому возникает нагрузка O(n²): n игроков оповещают n игроков. У атак дронов, порождающих много сообщений, эта нагрузка растёт ещё быстрее #### sp-spawn-burst · Лавина спавна при входе в людное место · Spawn burst when entering a crowd Когда игрок телепортируется в город, полный людей, сервер должен разом отправить внешность, экипировку и состояние сотен персонажей, которые стали видны. - Почему → Следствие → На экране: Игрок внезапно оказывается в людном месте после телепорта, входа в игру или смены канала → Полные данные по сотням персонажей собираются и отправляются за раз, и ПК игрока тоже загружает их разом → Сразу после прибытия короткий фриз, персонажи появляются с опозданием по одному, ввод запаздывает - Симптомы: Фриз, Задержка ввода, Перемотка / Факторы: Остановка, Задержка - У кого: Только у меня, Одна локация или канал / Когда: В движении и при смене локации, Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: отправлять в порядке близости, распределяя по нескольким тикам, кэшировать данные о внешности. Клиент: получать данные заранее, пока идёт экран загрузки, создавать полученных персонажей, распределяя по нескольким кадрам. - Цифры для ориентира: Если внешность, экипировка и баффы одного персонажа занимают 300 байт, то на 500 персонажей это около 150 KB. В один момент наваливается в десятки раз больше, чем обычно отправляется за тик (единицы KB). - На графике: Всплеск сразу после входа или техработ (исходящие байты по соединениям, время кадра на клиенте) - Где смотреть: Смотреть число байтов и пакетов, отправленных по соединению за первые секунды после прибытия в людное место (лог сервера), и время кадра на клиенте (нетграф, лог клиента) - Подтверждает: Сразу после прибытия исходящий трафик по соединению взлетает в десятки раз выше обычного тика и затем спадает, и в тот же момент скачет время кадра на клиенте - Опровергает: Если фриз такой же и при переходе в безлюдное место, дело в переходе между зонами (передаче на другой сервер) или в загрузке на клиенте - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [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 channel вместе с ним отправляются начальные данные вроде позиции и поворота, а если соединение забито, остальные акторы откладываются до следующего тика - [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 Если предметы на земле, призванные существа и отработавшие таймеры, которые должны исчезать, не удаляются и копятся, то чем дольше сервер работает, тем больше работы в каждом тике. - Почему → Следствие → На экране: Предметы на земле, призванные существа, истёкшие таймеры и данные пустых групп вовремя не удаляются → Списки, которые обходятся каждый тик, с каждым днём становятся длиннее → Сразу после техработ всё нормально, а через несколько дней именно этот сервер или локация постепенно начинает тормозить - Симптомы: Слоумо, Микрофризы, Задержка ввода / Факторы: Остановка - У кого: Весь сервер, Одна локация или канал / Когда: Чем дольше без перезапуска - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Записывать число объектов по локациям в метрики и следить за трендом, задать для объектов время жизни и предельное количество, регулярно проводить очистку. - Цифры для ориентира: Если сервер каждый тик обходит все объекты, то при удвоении числа объектов эта часть времени тика тоже удваивается. - На графике: Пила: плавный рост и резкий сброс (число объектов по зонам, время тика сервера) - Где смотреть: Смотреть число объектов (предметы на земле, призванные существа, таймеры) по зонам и серверам и время тика за период дольше цикла техработ (несколько недель) - Подтверждает: Число объектов и время тика начинают с низкого уровня после техработ, растут день ото дня и резко падают при техработах или перезапуске, и так по кругу, а памяти всё это время хватает - Опровергает: Если время тика не меняется, а растёт только память, дело в утечке памяти - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Как и утечка памяти, проблема усиливается со временем работы, но отличается тем, что памяти хватает, а растёт только время тика. Если график числа объектов имеет форму пилы с периодом техработ, это тот самый случай. - Источники: - [Actor Ticking in Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/actor-ticking-in-unreal-engine) · Epic Games · Если отдельно не задать интервал, акторы и компоненты выполняют тик раз в кадр, а когда он не нужен, тик можно отключить - [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 в облаке или буфер отправки → Сразу после патча в людных местах телепортация, «съеденные» умения и задержка ввода. В инфраструктуре ничего не меняли, а потерь стало больше - Симптомы: Телепортация, Съеденные действия / роллбэк, Задержка ввода / Факторы: Потери, Задержка - У кого: Весь сервер, Одна локация или канал, Один регион или провайдер / Когда: При наплыве игроков, Вечерний пик, Всегда - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда инфраструктуры · Сетевая инфраструктура - Команда разработки, задачи: Самим разбивать данные на пакеты не больше 1 200 байт. Для новых синхронизируемых полей отправлять только изменения и снижать частоту по расстоянию и важности. Перед деплоем сравнить на тестовом сервере число пакетов и байтов в секунду на игрока и размер самого большого пакета с предыдущей сборкой. Записывать версию сборки в метрики трафика. - Команда инфраструктуры, задачи: Серверы и ОС: отмечать время деплоя на графиках, сравнивать число пакетов и байтов в секунду на игрока и средний размер пакета до и после деплоя, настроить алерты по счётчикам превышения лимитов инстанса, при необходимости взять инстанс крупнее. Сеть: проверить пределы производительности файрвола, балансировщика нагрузки и оборудования защиты от DDoS и не блокируют ли они фрагменты. - Цифры для ориентира: Безопасный размер UDP-пакета не больше 1 200 байт. MTU пути в интернете обычно 1 500 байт, а через туннель ещё меньше (в GRE-туннеле 1 476 байт). Пакеты больше MTU пути фрагментируются или отбрасываются, а фрагментированный пакет теряется целиком при потере одного фрагмента. Если число пакетов в секунду на игрока растёт на 20%, на весь сервер оно тоже растёт на 20%, и инстанс, который работал близко к лимиту, сразу его превышает. - На графике: Ступенька вверх с определённого момента (пакеты и байты в секунду на игрока, средний размер пакета) - Где смотреть: Разделить пакеты и байты в секунду на NIC сервера (rxpck/s, txpck/s, rxkB/s, txkB/s в sar -n DEV, для EC2 NetworkPacketsOut и NetworkOut) на онлайн и сравнить до и после деплоя. Средний размер пакета: байты ÷ пакеты, распределение размеров: статистика Packet Lengths в Wireshark по захвату пакетов - Подтверждает: Сразу после деплоя пакеты и байты на игрока или средний размер пакета ступенькой поднимаются и так и остаются, и с того же момента растут число фрагментов, созданных сервером (fragcrt/s в sar -n IP), или счётчики превышения лимитов инстанса (pps_allowance_exceeded и bw_out_allowance_exceeded в AWS ENA) - Опровергает: Если характер трафика до и после деплоя одинаковый, а выросли только задержки и потери, смотреть изменения инфраструктуры в то же время (настройки, маршруты, оборудование, обновления ОС и ядра) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Если приходят жалобы «до патча всё было нормально», это первая причина на стороне игры, которую стоит проверить наряду с изменениями инфраструктуры. Даже если в патчноуте нет сетевых изменений, один новый эффект или синхронизируемое поле в людном месте умножается на сотни игроков. Где именно возросший трафик упирается в пределы, разобрано в причинах «IP-фрагментация UDP-пакетов», «Превышен лимит PPS в облаке», «Исчерпание пропускной способности NIC», «Нехватка буферов сокетов в ядре» и «Перегрузка промежуточного оборудования (файрвол, IPS, защита от DDoS)». Эта причина описывает случай, когда к пределу подвёл игровой патч, поэтому, прежде чем поднимать лимиты, сначала сокращают трафик, добавленный патчем. Если в то же время обновляли ОС и ядро, отличить эту причину от причины «Изменение производительности после обновления ОС, ядра, драйверов или прошивки» помогает то, изменился ли трафик на игрока. - Источники: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · UDP-приложениям не следует отправлять датаграммы больше MTU пути (SHOULD NOT). При потере одного фрагмента теряется весь фрагментированный пакет, а некоторые 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 · rxpck/s и txpck/s (пакеты в секунду), rxkB/s и txkB/s (KB в секунду) в sar -n DEV, fragcrt/s в sar -n IP (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 → Все игровые потоки останавливаются на время сборки (чем больше живых данных, тем дольше) → У всех игроков сервера одновременно фриз, затем перемотка - Симптомы: Фриз, Перемотка / Факторы: Остановка - У кого: Весь сервер / Когда: С постоянным периодом, Чем дольше без перезапуска - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Явно задать в параметрах запуска сборщик с короткими паузами (ZGC, Shenandoah, G1 с уменьшенной целевой паузой), сократить аллокации, подобрать размер кучи. - Команда инфраструктуры, задачи: Брать инстансы с запасом памяти под кучу, контейнерам выделять не меньше 2 CPU и около 1,8 GB памяти (при меньших ресурсах JDK 26 и ниже по умолчанию выбирает Serial GC), отслеживать длительность пауз GC. - Цифры для ориентира: Minor GC, который собирает только новые объекты (область Young), занимает от единиц до десятков ms. Full GC по всей куче с несколькими GB живых данных может длиться больше 1 секунды. У ZGC паузы короче 1 ms почти независимо от размера кучи, у Shenandoah паузы тоже короткие, потому что не растут вместе с кучей. - На графике: Всплески с постоянным периодом (время тика сервера, длительность пауз GC) - Где смотреть: Включить GC-лог и наложить моменты и длительность пауз на график времени тика сервера. Для Java смотреть строки Pause в логе, который включает параметр запуска -Xlog:gc* (в JDK 8 и ниже -XX:+PrintGCDetails), для .NET метрику пауз GC в dotnet-counters (с .NET 9 dotnet.gc.pause.time, в 8 и ниже % Time in GC since last GC), для Go строки, которые GODEBUG=gctrace=1 пишет на каждый GC - Подтверждает: Всплески времени тика совпадают с паузами GC, длительность пауз близка к длительности всплесков. Во всех зонах и каналах сервера всплеск в один и тот же момент - Опровергает: Если в GC-логе нет длинных пауз, а тик всё равно скачет, причина другая: блокировки, синхронные вызовы, запись на диск. Если скачет только одна зона, это GC скриптового движка (mem-script-gc) или нагрузка в этой зоне - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: У G1 в Java целевая длительность одной паузы по умолчанию 200 ms, а на 20-тиковом сервере это 4 тика. Если контейнеру дать меньше 2 CPU или меньше примерно 1,8 GB памяти, Java версии JDK 26 и ниже выбирает по умолчанию Serial GC, который собирает мусор в один поток, и паузы становятся намного длиннее. На серверах C# (.NET) обычно включают серверный и фоновый GC, но сборка поколений 0 и 1 (Gen0 и Gen1), где лежат новые объекты, и Full GC со сжатием кучи по-прежнему останавливают все потоки. В Go паузы обычно короче 1 ms, но при большом объёме аллокаций горутине, которая запрашивает память, приходится брать на себя часть работы 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 по умолчанию 200 ms (MaxGCPauseMillis). Если во время сборки кончается память, G1 переходит к Full GC с полной остановкой и сжатием всей кучи - [JEP 523: Make G1 the Default Garbage Collector in All Environments](https://openjdk.org/jeps/523) · OpenJDK · Раньше при 1 CPU или памяти меньше 1792 MB по умолчанию выбирался Serial GC, начиная с JDK 27 G1 используется по умолчанию везде - [JEP 439: Generational ZGC](https://openjdk.org/jeps/439) · OpenJDK · Паузы ZGC не дольше 1 ms и не зависят от размера кучи, паузы G1 от единиц ms до нескольких секунд. Если аллокации опережают освобождение памяти, возможна остановка аллокаций (allocation stall) - [JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)](https://openjdk.org/jeps/189) · OpenJDK · Паузы Shenandoah примерно одинаковы и при куче 200 MB, и при 200 GB - [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 · GC в Go работает в основном конкурентно, полные остановки короткие. При большом объёме аллокаций горутины берут на себя часть работы GC (assist), и появляются задержки - [JEP 271: Unified GC Logging](https://openjdk.org/jeps/271) · OpenJDK · Начиная с JDK 9 GC-лог переведён на единое журналирование (-Xlog). -Xlog:gc, как прежний -XX:+PrintGC, пишет по строке на каждый GC - [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, время по wall clock для каждой фазы, размер кучи в начале и в конце GC и целевой размер кучи #### mem-script-gc · Паузы GC в скриптовом движке · Scripting VM GC (Lua, etc.) Если на сервере, пусть даже написанном на C++, квесты, AI и умения выполняются скриптами (например, на Lua), то на время работы GC скриптового движка эта зона останавливается. - Почему → Следствие → На экране: В каждой зоне скриптовый движок выполняет квесты, AI и события и создаёт массу временных объектов → Когда GC скриптового движка собирает много за один раз, тик этой зоны останавливается → Короткие периодические подвисания только в определённой зоне или во время определённого события - Симптомы: Микрофризы, Фриз / Факторы: Остановка - У кого: Одна локация или канал / Когда: При наплыве игроков, С постоянным периодом - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Включить инкрементальный или поколенческий режим GC, выполнять сборку понемногу каждый тик, сократить временные объекты в скриптах. - Цифры для ориентира: Когда куча скриптов вырастает до сотен MB, сборка за один проход (с выключенной инкрементальной сборкой или полная сборка в поколенческом режиме) может занимать от десятков до сотен ms. - На графике: Всплески с постоянным периодом (время тика по зонам, память скриптового движка) - Где смотреть: Каждый тик записывать время тика по зонам и потребление памяти скриптовым движком этой зоны (в Lua collectgarbage("count")) и накладывать их на один график - Подтверждает: Резкие падения памяти скриптов (сборка за один проход) совпадают со всплесками тика в этой зоне, остальные зоны в порядке - Опровергает: Если тик скачет без изменений памяти скриптов, дело в нагрузке этой зоны или в блокировках. Если скачут сразу все зоны сервера, это 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 в минуту по GC-логу (Java -Xlog:gc, Go GODEBUG=gctrace=1), для .NET смотреть объём аллокаций и число сборок в dotnet-counters (с .NET 9 dotnet.gc.heap.total_allocated и dotnet.gc.collections, в 8 и ниже Allocation Rate и Gen 0 GC Count). Накладывать на онлайн и время событий - Подтверждает: С началом события скорость аллокаций и число сборок растут быстрее онлайна, короткие паузы учащаются. После события всё возвращается к прежнему уровню - Опровергает: Если число сборок прежнее, а каждая пауза стала длиннее, вырос объём живых данных (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 - [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) - Где смотреть: Смотреть память процесса игрового сервера (RSS в pidstat -r) на отрезке в несколько дней, а для сервера с GC смотреть, сколько кучи остаётся сразу после GC. В Java это значение после GC (строки -Xlog:gc показывают занятый объём до и после GC), в .NET размер кучи после GC в dotnet-counters (с .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) и ошибки страниц (page faults) #### mem-gc-thrash · Трешинг GC (мало свободного места в куче) · GC thrashing (heap nearly full) Когда живые данные приближаются к пределу кучи, GC почти нечего освобождать, и сборки идут одна за другой без перерыва. - Почему → Следствие → На экране: Из-за роста онлайна на событии или утечки живые данные заполняют кучу почти до предела → GC освобождает совсем немного, и сразу снова запускается Full GC, большую часть CPU занимает GC → Весь сервер несколько минут то уходит в слоумо, то ловит фризы, а затем падает из-за нехватки памяти - Симптомы: Слоумо, Фриз, Дисконнект / Факторы: Остановка - У кого: Весь сервер / Когда: Вечерний пик, При наплыве игроков, Чем дольше без перезапуска - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Задавать размер кучи с запасом относительно живых данных в пик (обычно как минимум в 2 раза больше), сокращать долгоживущие данные и утечки. - Команда инфраструктуры, задачи: Настроить алерт на долю времени GC, перезапускать сервер сразу, не дожидаясь, что он справится сам, брать инстансы с запасом памяти, чтобы можно было увеличить кучу. - Цифры для ориентира: Если GC занимает больше 10% времени работы, это обычно считают тревожным сигналом. Некоторые сборщики в Java выдают ошибку нехватки памяти, если тратят на GC 98% времени и почти ничего не освобождают. - На графике: Упор в лимит (плато) (куча сразу после GC, доля времени GC) - Где смотреть: Смотреть, насколько куча после GC близка к максимальной и какую долю времени занимает GC. Для Java смотреть «после GC(размер кучи)» в строках -Xlog: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 в куче есть запас, а паузы всё равно длинные, дело в выборе и настройке сборщика (mem-gc) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [The Parallel Collector](https://docs.oracle.com/en/java/javase/25/gctuning/parallel-collector1.html) · Oracle · Parallel GC выдаёт OutOfMemoryError, если тратит на GC больше 98% всего времени и освобождает меньше 2% кучи - [Garbage-First Garbage Collector Tuning](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-first-garbage-collector-tuning.html) · Oracle · По умолчанию (GCTimeRatio=12) G1 подбирает размер кучи так, чтобы 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 Когда памяти не хватает и ОС выгружает её часть на диск, при каждом обращении к этой памяти приходится ждать диск, который медленнее больше чем в 1 000 раз. - Почему → Следствие → На экране: Занятая память превышает физическую RAM → ОС выгружает часть памяти на диск и читает её обратно, когда она нужна → Время тика вырастает до сотен ms, у всех игроков сервера слоумо и фризы - Симптомы: Слоумо, Фриз / Факторы: Остановка - У кого: Весь сервер / Когда: Чем дольше без перезапуска, Вечерний пик - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Ограничить потребление памяти процессом (размер кучи и т. п.), проверять на утечки. - Команда инфраструктуры, задачи: Настроить игровые серверы так, чтобы они не использовали своп, реагировать по алертам на память. Без свопа процесс принудительно завершается (OOM) в тот же момент, когда памяти не хватает, поэтому RAM нужно брать с запасом сверх пикового потребления. - Цифры для ориентира: Чтение из RAM занимает около 100 ns, чтение выгруженной памяти обратно с SSD около 100 µs (в 1 000 раз дольше), с облачного диска по ту сторону сети около 1 ms (в 10 000 раз), с HDD 10 ms (в 100 000 раз). - На графике: Плавный рост (занятый своп, swap-in и swap-out) - Где смотреть: Накладывать на время тика столбцы si и so в vmstat 1 (сколько в секунду прочитано из свопа и выгружено в него), some и full в /proc/pressure/memory (доля времени, проведённого в ожидании памяти) и majflt/s в pidstat -r для процесса игрового сервера (ошибки страниц, при которых пришлось читать с диска) - Подтверждает: В моменты лагов si больше 0, вместе растут majflt/s игрового сервера и значение full в memory - Опровергает: Если si и so равны 0 и PSI по памяти около 0, своп ни при чём. Если свопа нет, а majflt/s и PSI растут, память кончилась и ОС перечитывает страницы кода, поэтому сначала нужно добавить или освободить память - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Сервер с GC при сборке читает кучу по всему объёму, поэтому, даже если в своп ушла только часть кучи, один цикл GC может растянуться до нескольких секунд или даже десятков секунд. Без свопа стадии замедления нет и процесс сразу принудительно завершается (OOM), поэтому сначала нужно обеспечить запас памяти. Даже без свопа, когда память почти кончилась, ОС выгружает из памяти страницы кода исполняемых файлов и читает их заново, и перед принудительным завершением весь сервер может какое-то время сильно тормозить. - Источники: - [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»: обращение к основной памяти 100 ns, позиционирование головки диска 10 ms (данные 2009 года) - [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html) · Linux kernel · swappiness: относительная стоимость свопа и освобождения файловых страниц, своп дорог, потому что это случайный ввод-вывод - [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 занимает около 100 µs - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · Задержка стандартного облачного диска (gp3): единицы ms - [vmstat(8) — Linux manual page](https://man7.org/linux/man-pages/man8/vmstat.8.html) · procps-ng · si: память, прочитанная из свопа за секунду, so: память, выгруженная в своп за секунду - [PSI - Pressure Stall Information](https://docs.kernel.org/accounting/psi.html) · Linux kernel · some в /proc/pressure/memory (доля времени, когда стояла часть задач) и 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 раз медленнее) → Та же работа обходится тику в несколько раз дороже, в тяжёлых случаях слоумо - Симптомы: Слоумо / Факторы: Остановка - У кого: Весь сервер / Когда: Всегда, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Размещать данные, которые часто используются вместе, подряд в памяти (data-oriented design). - На графике: Высоко с самого начала (время тика, загрузка CPU) - Где смотреть: Запустить perf stat -d -p PID на процессе игрового сервера, измерить число инструкций за такт (insn per cycle) и промахи кэшей L1 и LLC, смотреть вместе со временем тика и загрузкой CPU - Подтверждает: CPU постоянно загружен, но insn per cycle низкий и промахов LLC много. Если в сборке с изменённым размещением данных время тика при том же онлайне заметно падает, причина подтверждена - Опровергает: Если загрузка CPU низкая, а тик медленный, дело в ожидании вне 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,5 ns, кэш L2: 7 ns, основная память: 100 ns (данные 2009 года). Обращение к RAM на один-два порядка медленнее, чем к кэшу - [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 Когда из-за постоянных выделений и освобождений свободное место дробится на мелкие куски, процесс занимает намного больше памяти, чем реально использует. - Почему → Следствие → На экране: Много потоков долго выделяют и освобождают блоки памяти разного размера → Свободное место раздроблено, вернуть его ОС нельзя, и потребление растёт, как при утечке → Чем дольше работает сервер, тем сильнее он тормозит из-за свопа и нехватки памяти, а затем принудительно завершается - Симптомы: Слоумо, Дисконнект / Факторы: Остановка - У кого: Весь сервер / Когда: Чем дольше без перезапуска - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Завести пулы памяти по размерам блоков, перейти на аллокатор, устойчивый к фрагментации (jemalloc, mimalloc и др.). - На графике: Плавный рост (память процесса (RSS)) - Где смотреть: Запустить два сервера одной сборки, на одном уменьшить число арен glibc переменной окружения MALLOC_ARENA_MAX или заменить аллокатор (например, на jemalloc) и несколько дней сравнивать RSS в pidstat -r - Подтверждает: При сопоставимом онлайне и числе объектов рост RSS останавливается или заметно замедляется только на изменённом сервере - Опровергает: Если и с другим аллокатором память растёт так же, это память, которую не освобождают (mem-leak) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Внешне это выглядит как утечка, но анализ кучи места утечки не покажет. Со стандартным аллокатором Linux (glibc) на серверах с большим числом потоков фрагментация особенно сильна, и одна только замена аллокатора иногда заметно снижает потребление. - Источники: - [mallopt(3) — Linux manual page](https://man7.org/linux/man-pages/man3/mallopt.3.html) · Linux man-pages · Чтобы потоки меньше конкурировали, malloc в glibc создаёт арены, число которых может доходить до кратного числу 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 На сервере с двумя процессорами обращение к памяти, подключённой к другому процессору, идёт медленнее. - Почему → Следствие → На экране: Потоки и их память оказываются на разных процессорных сокетах → Обращения к памяти замедляются (в 1,5–2 раза в зависимости от оборудования) → На одинаковом железе процессы работают с разной скоростью - Симптомы: Слоумо / Факторы: Остановка - У кого: Весь сервер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Привязать процесс и его память к одному сокету (numactl), а на двухсокетном сервере распределить процессы игрового сервера по сокетам. - На графике: Высоко только у некоторых (время тика по процессам, память по узлам NUMA) - Где смотреть: Смотреть через numastat -p PID, на каком узле NUMA лежит память процесса игрового сервера, растут ли numa_miss и other_node в numastat, и сравнивать с узлом CPU, на котором работает процесс - Подтверждает: Только у медленных процессов большая часть памяти лежит на другом узле, чем их CPU, а после перезапуска с привязкой CPU и памяти к одному узлу через numactl разница исчезает - Опровергает: Если размещение по узлам такое же, как у быстрых процессов, а процесс всё равно медленный, причина другая: шумный сосед, троттлинг 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 Если игровой поток на каждой строке лога ждёт, пока диск завершит запись, то при занятом диске останавливается и игра. - Почему → Следствие → На экране: Боевые логи и логи обменов пишутся в файл прямо из игрового потока → Если требуется гарантированная запись (fsync) или буфер записи ОС (страничный кэш) заполнен до предела, при занятом диске одна запись длится десятки ms → Подвисания в боях, где пишется много логов - Симптомы: Микрофризы, Фриз / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Перейти на асинхронное логирование (буфер в памяти и отдельный поток), сократить объём логов, не вызывать fsync в игровом потоке. - Команда инфраструктуры, задачи: Запускать ротацию и сжатие логов с пониженным приоритетом ввода-вывода, держать логи на отдельном от данных диске, отслеживать задержку записи на диск. - На графике: Случайные всплески (время тика сервера, задержка записи на диск) - Где смотреть: Накладывать w_await и aqu-sz из iostat -x 1 на время тика и через perf trace -p PID --duration 10 искать в игровом сервере вызовы write и fsync дольше 10 ms и потоки, которые их сделали - Подтверждает: В моменты всплесков тика вызовы write и fsync в игровом потоке длятся десятки ms, и в тот же момент подскакивает задержка записи на диск. Часто это совпадает со временем ротации или сжатия логов - Опровергает: Если в игровом потоке нет долгих системных вызовов, а тик скачет, причина другая: GC, блокировки, превышение бюджета тика. Если долгие вызовы только в отдельном потоке логирования, на игру это не влияет - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Обычно ОС сначала принимает запись в память (страничный кэш) и сбрасывает её на диск позже, поэтому строка лога, как правило, записывается мгновенно. Остановки бывают, когда fsync требует гарантированной записи, когда накопившиеся записи превышают лимит и ОС блокирует вызов записи, а также во время ротации или сжатия файлов логов. Поэтому обычно всё в порядке, а всплески появляются только в моменты, когда диск занят. - Источники: - [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 получает время диска, только когда диском не пользуются другие программы - [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 Запрос записать данные на диск «гарантированно» занимает от 0,1 ms до десятков ms в зависимости от диска, а когда таких запросов много, очередь растёт. - Почему → Следствие → На экране: Периодические сохранения и массовый выход из игры дают пачку запросов на гарантированную запись → Очередь к диску растёт → Лаги в моменты сохранения, задержки при выходе из игры и смене канала - Симптомы: Микрофризы, Задержка ввода / Факторы: Остановка, Задержка - У кого: Весь сервер / Когда: С постоянным периодом, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Группировать сохранения (несколько сохранений на один fsync), разносить сохранения по времени. - Команда инфраструктуры, задачи: Серверы и ОС: серверные SSD с защитой от потери питания, мониторинг длины очереди к диску и задержки fsync. Серверы БД: если сохранения идут в БД, диск журнала БД тоже на таком SSD, мониторинг задержки коммитов. - Цифры для ориентира: Время одного вызова зависит от оборудования, но примерно так: серверный SSD (с защитой от потери питания) 0,1 ms, обычный SSD от 1 до нескольких ms, облачный диск 1–2 ms, HDD 10 ms и больше. Если один поток ждёт вызовы по одному, HDD не успевает выполнить и 100 вызовов в секунду. - На графике: Всплески с постоянным периодом (длина очереди к диску, задержка сброса (flush) и записи) - Где смотреть: Накладывать на время периодических сохранений и выходов из игры f/s и f_await из iostat -x 1 (число сбросов, обработанных диском, и их длительность), а также w/s, aqu-sz и w_await. Старые версии sysstat показывают aqu-sz как avgqu-sz. Для облачного диска смотреть VolumeQueueLength и VolumeAvgWriteLatency в EBS - Подтверждает: В моменты сохранений и массового выхода число сбросов и длина очереди растут вместе, w_await и f_await в несколько раз выше обычного. В это время задерживаются сохранения и смена канала - Опровергает: Если очередь растёт в моменты, не связанные с сохранениями и выходами, это бэкап или сжатие (dk-backup) либо лимит IOPS (dk-iops). Если число сбросов прежнее, а всё замедлилось, вероятнее исчерпание burst-кредитов (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): единицы ms, у io2 Block Express в среднем меньше 500 µs на операцию размером 16 KiB - [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 10 ms - [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-кредитов облачного диска · Burst credit depletion У некоторых облачных дисков и небольших конфигураций серверов есть burst-кредиты, которые позволяют какое-то время работать быстрее базовой производительности. Если высокая нагрузка держится долго и кредиты кончаются, скорость резко падает. - Почему → Следствие → На экране: Диск долго работает выше базовой производительности → Burst-кредиты кончаются, производительность резко падает до базовой → Каждый вечер через несколько часов нагрузки начинаются лаги - Симптомы: Микрофризы, Слоумо, Задержка ввода / Факторы: Остановка, Задержка - У кого: Весь сервер / Когда: Вечерний пик, Чем дольше без перезапуска - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда инфраструктуры, задачи: Серверы и ОС: диски с гарантированной производительностью (gp3, тип с заданным IOPS), алерт на остаток кредитов, проверка лимита burst для дисковой пропускной способности инстанса и CPU-кредитов. Серверы БД: перевести диски БД, включая управляемые БД, на тип с гарантированной производительностью, алерт на остаток кредитов. - Цифры для ориентира: Диск AWS gp2 на 100 GB обычно даёт 300 IOPS, в режиме burst 3 000 IOPS, и при полном запасе кредитов держит burst около 30 минут. gp3 всегда даёт 3 000 IOPS без всяких кредитов. Небольшие диски Azure Premium SSD тоже работают в режиме burst за счёт кредитов до 30 минут. - На графике: Упор в лимит (плато) (IOPS, остаток burst-кредитов) - Где смотреть: Смотреть в CloudWatch EBS BurstBalance (gp2, st1, sc1), EBSIOBalance% и EBSByteBalance% инстанса (у части инстансов с burst), CPUCreditBalance у burst-инстансов. В Azure смотреть метрики использования burst-кредитов, например Data Disk Used Burst IO Credits Percentage - Подтверждает: С момента, когда остаток падает почти до 0, IOPS (VolumeReadOps, VolumeWriteOps) выходит на плато на уровне базовой производительности, а VolumeQueueLength и лаги растут вместе. Начинается после нескольких часов пика - Опровергает: Если запас всех кредитов большой, а IOPS стоит на плато, это фиксированный лимит тома или инстанса (dk-iops) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Даже если с диском всё в порядке, у небольших виртуальных серверов есть лимит burst на саму дисковую пропускную способность инстанса (например, не меньше 30 минут в сутки), и картина получается такая же. Дешёвые серверы с CPU-кредитами, когда кредиты кончаются, тоже замедляются до базовой производительности. - Источники: - [Amazon EBS General Purpose SSD volumes](https://docs.aws.amazon.com/ebs/latest/userguide/general-purpose.html) · AWS · Базовая производительность gp2: 3 IOPS на GiB (минимум 100), burst до 3 000 IOPS за счёт I/O-кредитов, 5 400 000 кредитов хватает минимум на 30 минут. gp3 всегда даёт 3 000 IOPS без burst - [Managed disk bursting](https://learn.microsoft.com/en-us/azure/virtual-machines/disk-bursting) · Microsoft Azure · Premium SSD P20 и меньше используют burst на основе кредитов, при полном запасе кредитов максимальная скорость burst держится 30 минут - [Amazon EBS-optimized instance types](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-optimized.html) · AWS · Некоторые инстансы держат максимальную производительность EBS только 30 минут раз в 24 часа, затем возвращаются к базовой - [Standard mode for burstable performance instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-standard-mode.html) · AWS · Burst-инстанс в стандартном режиме, когда CPU-кредиты кончаются, снижает загрузку CPU до базового уровня (плавно, без резкого падения) - [Amazon CloudWatch metrics for Amazon EBS](https://docs.aws.amazon.com/ebs/latest/userguide/using_cloudwatch_ebs.html) · AWS · BurstBalance: остаток (%) I/O-кредитов gp2 и кредитов пропускной способности 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%: остаток кредитов EBS у некоторых инстансов с burst на 30 минут раз в 24 часа, CPUCreditBalance: остаток CPU-кредитов burst-инстанса - [Disk metrics](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-metrics) · Microsoft Azure · Data Disk Used Burst IO Credits Percentage и другие метрики использования burst-кредитов дисков и ВМ (интервал 5 минут) #### dk-iops · Лимит IOPS и насыщение очереди · IOPS limit / queue saturation Если запросов больше, чем диск может обработать за секунду, очередь растёт и задержка резко увеличивается. - Почему → Следствие → На экране: Запросы на чтение и запись приближаются к пределу производительности диска → Очередь растёт (обычно резко при утилизации от 90%) → Задержки сохранения и загрузки, а при синхронных вызовах фризы - Симптомы: Задержка ввода, Фриз / Факторы: Задержка, Остановка - У кого: Весь сервер / Когда: При наплыве игроков, Вечерний пик - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера, Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Объединять запросы, кэшировать часто читаемые данные, делать ввод-вывод асинхронным, чтобы игровой поток не ждал диск. - Команда инфраструктуры, задачи: Серверы и ОС: более быстрые диски, проверка лимитов дисковой пропускной способности и IOPS для типа инстанса, алерты на утилизацию и очередь диска, копирование больших файлов в часы низкой нагрузки. Серверы БД: алерты на утилизацию IOPS и пропускной способности дисков БД, проверка дисковых лимитов конфигурации инстанса БД. - Цифры для ориентира: HDD около 150 IOPS, SATA SSD десятки тысяч, NVMe сотни тысяч. Стандартный облачный диск (AWS gp3) даёт 3 000 IOPS и 125 MiB/s, и если копирование большого файла исчерпывает лимит на объём передачи в секунду, который не зависит от IOPS, застревают даже мелкие записи. - На графике: Упор в лимит (плато) (IOPS, длина очереди к диску) - Где смотреть: Смотреть r/s, w/s, rkB/s, wkB/s, aqu-sz, r_await и w_await в iostat -x 1. В облаке смотреть VolumeReadOps, VolumeWriteOps, VolumeQueueLength в EBS и признаки превышения лимита 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 · Случайное чтение блоками 4K на серверном HDD 7 200 rpm: 170 IOPS (QD16) - [D3-S4520 SSD](https://www.solidigm.com/products/data-center/d3/s4520.html) · Solidigm · Серверный SATA SSD: случайное чтение и запись блоками 4 KB до 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 и 125 MiB/s, это два отдельных лимита, каждый можно увеличить независимо - [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: 1, если была попытка превысить лимит IOPS или объёма передачи тома (инстансы 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: 1, если была попытка превысить лимит EBS по IOPS или объёму передачи для инстанса #### dk-full · Диск заполнен · Disk full Когда логи и дампы заполняют диск, запись завершается ошибкой, и если к этому не подготовиться, сервер падает. - Почему → Следствие → На экране: Логи, дампы и временные файлы заполняют диск на 100% → Запись не удаётся. Без обработки ошибок сервер падает, с обработкой не проходят сохранения → Дисконнект, роллбэк прогресса - Симптомы: Дисконнект, Съеденные действия / роллбэк / Факторы: Остановка - У кого: Весь сервер / Когда: Чем дольше без перезапуска - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда инфраструктуры · Инфраструктура БД, Команда разработки · Разработка сервера - Команда разработки, задачи: Обрабатывать ошибки записи: повторять сохранение и поднимать алерт вместо падения сервера, сократить ненужные логи и дампы. - Команда инфраструктуры, задачи: Серверы и ОС: ротация логов, алерты на заполнение, раздельные диски для логов и данных. Серверы БД: следить, чтобы журнал транзакций БД (WAL, binlog) не копился из-за остановки репликации или пропущенного бэкапа журнала. - На графике: Плавный рост (заполненность диска) - Где смотреть: Смотреть заполненность в df -h и заполненность inode в df -i, искать ошибки ENOSPC в логах сервера и БД. Для БД смотреть слоты, у которых active равно false, в pg_replication_slots (PostgreSQL), число и размер файлов в SHOW BINARY LOGS (MySQL), log_reuse_wait_desc в sys.databases (SQL Server), FreeStorageSpace (RDS) - Подтверждает: Заполненность несколько дней стабильно растёт, момент достижения 100% совпадает с падением сервера или сбоями сохранения, в логах есть ENOSPC - Опровергает: Если места достаточно, а запись не проходит, причина другая: права доступа, лимит размера файла и т. п. - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Журнал транзакций БД (WAL, binlog и др.) не удаляется и продолжает расти, если реплика остановилась или бэкап журнала не выполнился. Когда этот диск заполняется, в БД останавливаются все записи, и сохранения и обмены разом перестают проходить. - Источники: - [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 заполняется, сервер БД может аварийно завершиться с PANIC - [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 · Когда журнал заполнен, БД доступна только для чтения, изменять данные нельзя. Частые причины, которые мешают очистке журнала: пропущенный бэкап журнала, отставание репликации, длинные транзакции. Что именно мешает, показывает log_reuse_wait_desc в sys.databases - [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: оставшееся место в хранилище инстанса БД #### dk-backup · Бэкап, сжатие и сканирование · Backup / compression / scans Когда ночной бэкап, сжатие логов или проверка безопасности занимают диск целиком, чтение и запись игрового сервера застревают. - Почему → Следствие → На экране: Запускается плановый бэкап или сжатие → Задание забирает большую часть пропускной способности диска и IOPS → Лаги каждый день в одно и то же время - Симптомы: Микрофризы, Задержка ввода / Факторы: Остановка, Задержка - У кого: Весь сервер / Когда: С постоянным периодом - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда инфраструктуры, задачи: Серверы и ОС: понизить приоритет ввода-вывода для бэкапа, сжатия и сканирования, разнести их по времени. Серверы БД: делать бэкап с реплики. - На графике: Всплески с постоянным периодом (утилизация диска, время ожидания диска) - Где смотреть: Через sar -d наложить по дням %util, await и aqu-sz из записей за последние несколько дней (суточные файлы в /var/log/sa, sadc должен собирать и данные дисков с ключом -S DISK), в эти же моменты через pidstat -d 1 найти процессы с наибольшими kB_rd/s и kB_wr/s и сверить с расписанием cron и таймеров systemd - Подтверждает: Каждый день в одно и то же время подскакивают await и %util, и в это время большую часть чтения и записи на диск занимают процессы бэкапа, сжатия или сканирования - Опровергает: Если время всплесков каждый день разное, плановое задание маловероятно. Если в эти моменты основную часть ввода-вывода даёт сам игровой сервер, дело в сохранениях и логах (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: await, aqu-sz и %util по устройствам из суточных файлов (по умолчанию /var/log/sa), данные дисков нужно собирать ключом -S DISK у sadc - [pidstat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/pidstat.1.html) · sysstat · -d: kB_rd/s и kB_wr/s по процессам (объём чтения и записи на диск в секунду) #### dk-lazy-load · Ленивая загрузка на сервере · Lazy loading on the server Если сервер читает данные данжа или карты с диска в момент первого запроса, на этот тик останавливаются все. - Почему → Следствие → На экране: Кто-то впервые входит в данж или локацию → Сервер читает данные с диска в игровом потоке → У всех на этом сервере короткий фриз - Симптомы: Фриз / Факторы: Остановка - У кого: Одна локация или канал, Весь сервер / Когда: В движении и при смене локации - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Загружать данные заранее при старте сервера, загружать асинхронно. - Команда инфраструктуры, задачи: Сервер, только что созданный из снапшота, перед вводом в работу прогревать (один раз прочитать все блоки диска) или использовать быстрое восстановление из снапшота. - На графике: Случайные всплески (время тика сервера, чтение с диска) - Где смотреть: Сопоставить моменты остановок с записями о первом входе в данж или локацию в логе игрового сервера и смотреть в эти моменты чтение с диска игровым сервером (kB_rd/s в pidstat -d) и долгие вызовы read и open через perf trace --duration. Если облачный сервер только что поднят, сравнить VolumeAvgReadLatency в EBS со старыми серверами - Подтверждает: Остановка только в момент первого входа, при повторном входе туда же остановки нет. Во время остановки игровой поток ждёт чтения файла - Опровергает: Если такие же остановки бывают и в уже загруженных локациях, причина другая: превышение бюджета тика, 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 по процессам (объём чтения с диска в секунду) - [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 · Core dump writing При падении сервер пишет на диск несколько GB памяти, и перезапуск может задерживаться на несколько минут. - Почему → Следствие → На экране: Сервер падает и записывает всю память в файл → Пока пишутся несколько GB, перезапустить сервер нельзя → Сервер падает, у всех дисконнект, после чего ещё долго ошибка входа - Симптомы: Ошибка входа / бесконечная загрузка / Факторы: Остановка - У кого: Весь сервер / Когда: Изредка, случайно - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Рассмотреть мини-дампы, где хранится только нужная часть памяти, исправить причину падения. - Команда инфраструктуры, задачи: Ограничить размер дампа (настройки core dump в ОС), использовать быстрый диск, отделить перезапуск от дампа (сжатие и выгрузку дампа делать отдельно после перезапуска). - На графике: Массовый обрыв соединений (число подключений, время перезапуска сервера) - Где смотреть: Сопоставить время падения, размер core-файла (coredumpctl list и info или файл там, куда указывает core_pattern), время окончания записи файла и время, когда сервис снова поднялся, и смотреть в этом интервале wkB/s в iostat -x - Подтверждает: После падения, пока пишется core-файл на несколько GB, запись на диск держится около лимита, и перезапуск начинается только после окончания записи - Опровергает: Если core dump выключен или получился маленьким, а перезапуск всё равно долгий, дело в процессе старта сервера: загрузка карт, холодный кэш БД (db-cold-cache) и т. п. - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [core(5) — Linux manual page](https://man7.org/linux/man-pages/man5/core.5.html) · Linux man-pages · RLIMIT_CORE задаёт предел размера core-файла, coredump_filter выбирает, какие области памяти включать, core dump можно передать по конвейеру в программу и обработать отдельно - [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: список core dump в журнале (TIME: время падения по данным ядра), info: подробности по каждому дампу и записанный на диск размер - [iostat(1) — Linux manual page](https://man7.org/linux/man-pages/man1/iostat.1.html) · sysstat · -x: wkB/s (объём записи на диск в секунду) #### dk-hdd · Задержка позиционирования HDD · HDD seek latency У HDD головка должна перемещаться над пластинами (позиционирование, seek), поэтому каждое чтение или запись разбросанных данных занимает почти 10 ms. - Почему → Следствие → На экране: HDD на старых серверах или в дешёвых хранилищах → Около 10 ms на каждое случайное чтение или запись → Сохранение и загрузка в целом идут медленно - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Весь сервер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда инфраструктуры · Инфраструктура БД, Команда разработки · Разработка сервера - Команда разработки, задачи: Проектировать запись так, чтобы она была в основном последовательной. - Команда инфраструктуры, задачи: Серверы и ОС: заменить на SSD (начиная с дисков для сохранений, где больше всего случайного чтения и записи). Серверы БД: в первую очередь перевести на SSD диски БД с наибольшим объёмом случайного чтения и записи. - На графике: Высоко с самого начала (задержка чтения и записи на диск (r_await, w_await)) - Где смотреть: Проверить через lsblk -d -o NAME,ROTA, вращающийся ли это диск (HDD), и смотреть r/s, w/s, r_await и w_await в iostat -x 1. Для виртуального сервера тип диска смотреть в характеристиках облака или хранилища - Подтверждает: Диск вращающийся, и при нагрузке всего от десятков до сотни с небольшим запросов в секунду r_await и w_await постоянно держатся на уровне от единиц до десятков ms - Опровергает: Если диск SSD, а задержка высокая, вероятнее насыщение очереди (dk-iops) или исчерпание burst-кредитов (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 · Одно позиционирование головки диска 10 ms, последовательное чтение 1 MB с диска 20 ms - [Exos X18 Data Sheet](https://www.seagate.com/www-content/datasheets/pdfs/exos-x18-mango-DS2045-1N-2007US-en_US.pdf) · Seagate · Средняя задержка вращения HDD 7 200 rpm 4,16 ms, случайное чтение блоками 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 Без индекса, чтобы найти строки по условию, приходится читать всю таблицу (полное сканирование). - Почему → Следствие → На экране: С деплоем новой функции появляется поиск по условию, для которого нет индекса → Сканируются все миллионы строк, один запрос занимает от сотен ms до нескольких секунд → Долго загружаются почта и история обменов, соединения заняты, и в очереди стоят даже другие запросы - Симптомы: Задержка ввода, Ошибка входа / бесконечная загрузка / Факторы: Задержка, Остановка - У кого: Только одна функция, Весь сервер / Когда: При определённом действии - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Перед деплоем проверять план выполнения новых запросов, добавлять индексы, проверять, что изменяющие запросы (UPDATE, DELETE) тоже используют индекс. - Команда инфраструктуры, задачи: Следить за журналом медленных запросов, находить запросы с полным сканированием и передавать их команде разработки, индексы на работающей БД добавлять онлайн-способом с короткими блокировками. - Цифры для ориентира: С индексом запрос занимает единицы ms, а без индекса замедляется пропорционально объёму данных, на больших таблицах в сотни и даже десятки тысяч раз. - На графике: Ступенька вверх с определённого момента (задержка запросов к БД, число прочитанных строк) - Где смотреть: В MySQL смотреть Rows_examined и Rows_sent в slow query log (с включённым log_queries_not_using_indexes туда пишутся и запросы без индекса), SUM_NO_INDEX_USED и SUM_ROWS_EXAMINED в performance_schema events_statements_summary_by_digest и запускать EXPLAIN. В PostgreSQL смотреть seq_scan и seq_tup_read в pg_stat_user_tables и запускать EXPLAIN - Подтверждает: Новый после деплоя запрос читает строк (Rows_examined) в тысячи раз больше, чем возвращает (Rows_sent), EXPLAIN показывает полное сканирование таблицы (в MySQL type ALL, в PostgreSQL Seq Scan). seq_tup_read у большой таблицы резко растёт с момента деплоя - Опровергает: Если индекс используется, а запрос всё равно медленный, это ожидание блокировок (db-hot-row, db-ddl-lock) или смена плана выполнения (db-plan-flip). Полное сканирование маленькой таблицы может быть нормой - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Медленнее становится не только чтение. Изменяющий запрос (UPDATE, DELETE) без индекса в некоторых БД блокирует все просканированные строки и может задержать сохранения даже тех игроков, которых этот запрос не касается. - Источники: - [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 · seq_scan (число последовательных сканирований) и seq_tup_read (число строк, прочитанных последовательным сканированием) в pg_stat_user_tables - [Using EXPLAIN (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/using-explain.html) · PostgreSQL · Seq Scan: план выполнения, при котором по порядку читаются все строки таблицы #### db-hot-row · Конкуренция за блокировку горячей строки · Hot row lock contention Когда все пытаются изменить одну и ту же строку (гильдейский склад, популярный лот на аукционе, общий счётчик сервера), блокировку получает только один запрос за раз. - Почему → Следствие → На экране: Из-за события или популярного предмета все изменения приходятся на одну и ту же строку → Запросы ждут, пока получат блокировку → Сбои обменов, «Повторите попытку позже», таймауты - Симптомы: Съеденные действия / роллбэк, Задержка ввода / Факторы: Остановка, Задержка - У кого: Только одна функция / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Разделить строку на несколько (шардированный счётчик), сделать транзакции короче, накапливать изменения в памяти и применять одним разом. - Команда инфраструктуры, задачи: Мониторить время и число ожиданий блокировок строк, находить строки, за которые идёт борьба, и сообщать о них команде разработки. - Цифры для ориентира: Если один запрос держит блокировку 10 ms, строку можно изменить не больше 100 раз в секунду. Если внутри транзакции есть обращение к другому серверу и обратно, это число падает ещё сильнее. - На графике: Растёт вслед за онлайном и нагрузкой (число и время ожиданий блокировок строк) - Где смотреть: В MySQL смотреть прирост Innodb_row_lock_waits и Innodb_row_lock_time и значение Innodb_row_lock_current_waits, а через sys.innodb_lock_waits выяснять, кто кого ждёт. В PostgreSQL смотреть сессии, у которых wait_event_type равен Lock, в pg_stat_activity и запросы, у которых granted равно false, в pg_locks, а если включить log_lock_waits (по умолчанию выключен), долгие ожидания блокировок попадают в лог - Подтверждает: Ожидания блокировок резко растут вслед за событием и онлайном, и большинство ждущих запросов указывает на одну и ту же строку (один ключ) одной таблицы - Опровергает: Если ожидания равномерно распределены по многим таблицам и строкам, дело в насыщении диска или CPU. Если одна сессия долго держит блокировку и не отпускает её, это долго открытая транзакция (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 · wait_event_type в pg_stat_activity: значение 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 · Дедлок в БД · Database deadlock Если две транзакции (операции БД, которые выполняются как единое целое) ждут строки, заблокированные друг другом, БД принудительно отменяет одну из них. - Почему → Следствие → На экране: Обмен A блокирует строки в порядке «предмет → валюта», обмен B в порядке «валюта → предмет» → БД обнаруживает дедлок и делает роллбэк одной из транзакций → Обмены и крафт иногда срываются, предметы возвращаются назад - Симптомы: Съеденные действия / роллбэк, Задержка ввода / Факторы: Потери, Остановка - У кого: Только одна функция / Когда: При наплыве игроков, При определённом действии - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Захватывать блокировки в едином порядке, делать транзакции короче, автоматически повторять при сбое. - Команда инфраструктуры, задачи: Держать обнаружение дедлоков включённым, собирать записи о дедлоках и передавать их команде разработки, на серверах MySQL с отключённым обнаружением уменьшить лимит ожидания блокировки (по умолчанию 50 секунд). - Цифры для ориентира: В MySQL (InnoDB) дедлок обнаруживается почти мгновенно, в PostgreSQL по умолчанию через 1 секунду, в SQL Server максимум примерно через 5 секунд. Всё это время оба запроса стоят. Если на сервере MySQL обнаружение отключено из-за очень большого числа одновременных запросов, ожидание длится до лимита ожидания блокировки (по умолчанию 50 секунд). - На графике: Случайные всплески (число дедлоков, число сбоев обменов) - Где смотреть: В MySQL смотреть LATEST DETECTED DEADLOCK в SHOW ENGINE INNODB STATUS (только последний случай), все дедлоки в журнале ошибок при включённом innodb_print_all_deadlocks и lock_deadlocks в INFORMATION_SCHEMA.INNODB_METRICS. В PostgreSQL смотреть deadlocks в pg_stat_database, в SQL Server xml_deadlock_report в сессии system_health, включённой по умолчанию. Коды ошибок на стороне игрового сервера: MySQL 1213, PostgreSQL 40P01, SQL Server 1205 - Подтверждает: В моменты сбоев обменов и крафта число дедлоков растёт, а две записанные транзакции блокируют одни и те же таблицы в противоположном порядке - Опровергает: Если число дедлоков не меняется, а сбои есть, это превышение лимита ожидания блокировки (ошибка 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 секунд, при частых дедлоках он сокращается до 100 ms. Сессия 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: две транзакции последнего дедлока, удерживаемые и ожидаемые блокировки, транзакция, для которой сделан роллбэк - [InnoDB INFORMATION_SCHEMA Metrics Table](https://dev.mysql.com/doc/refman/8.4/en/innodb-information-schema-metrics-table.html) · MySQL · Счётчик lock_deadlocks в INNODB_METRICS (включён по умолчанию) - [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 · deadlocks в pg_stat_database: число дедлоков, обнаруженных в этой БД - [PostgreSQL Error Codes (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/errcodes-appendix.html) · PostgreSQL · 40P01 deadlock_detected #### db-pool · Исчерпание пула соединений · Connection pool exhaustion Число соединений с БД фиксировано, поэтому, когда медленные запросы занимают соединения, остальные запросы ждут. - Почему → Следствие → На экране: Медленные запросы или наплыв запросов занимают все соединения → Новые запросы ждут, пока освободится соединение → Бесконечная загрузка при входе, задержки сохранения, таймауты - Симптомы: Ошибка входа / бесконечная загрузка, Задержка ввода / Факторы: Остановка, Задержка - У кого: Весь сервер, Только одна функция / Когда: Сразу после входа или техработ, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Убрать медленные запросы, настроить размер пула и таймаут ожидания (не раздувать пул без оглядки), разделить пулы по функциям. - Команда инфраструктуры, задачи: Проверить максимальное число соединений БД и запас по CPU и IOPS, перед добавлением серверов и автомасштабированием проверять, что число серверов × размер пула не превышает максимум соединений, добавить в мониторинг метрики ожидания соединений и блокировок. - Цифры для ориентира: Нужное число соединений прикидывают как «запросов в секунду × время, на которое один запрос занимает соединение». При 2 000 запросов в секунду по 5 ms в среднем постоянно заняты 10 соединений. С учётом пиков обычно берут в два-три раза больше. Если запрос замедлится до 150 ms, при той же нагрузке понадобится 300 соединений. - На графике: Упор в лимит (плато) (число занятых соединений с БД, время ожидания соединения) - Где смотреть: Считать состояние соединений по каждому игровому серверу на стороне БД. В MySQL смотреть Host, Command (у простаивающих соединений Sleep) и Time в SHOW PROCESSLIST, Threads_connected и Threads_running, а также число отклонённых соединений Connection_errors_max_connections. В PostgreSQL считать pg_stat_activity с группировкой по client_addr и state. Если библиотека пула соединений в игровом сервере отдаёт число ожидающих запросов и время ожидания, смотреть и их - Подтверждает: Все соединения одного игрового сервера (столько, сколько позволяет пул) выполняют запросы, свободных 0, и в это время вход и сохранения ждут. Либо общее число соединений с БД дошло до max_connections, и новые соединения отклоняются - Опровергает: Если свободных соединений достаточно, а всё медленно, дело в задержке самих запросов (db-no-index, db-hot-row) или в нехватке ресурсов БД - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Если без оглядки раздувать пул, растёт только нагрузка на CPU БД и конкуренция за блокировки, и замедляются все. А если число серверов × размер пула превышает максимум соединений БД, новые или перезапущенные серверы не могут даже подключиться. Это часто случается при автомасштабировании и сразу после техработ. - Источники: - [Number Of Database Connections](https://wiki.postgresql.org/wiki/Number_Of_Database_Connections) · PostgreSQL · Когда ресурсы БД исчерпаны, рост числа соединений даже снижает производительность. И для задержки, и для пропускной способности лучше держать столько активных соединений, сколько позволяют ресурсы, а остальные запросы держать в очереди - [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 Если запись идёт в основную БД, а чтение с реплики, то при отставании реплики только что записанные данные не видны. - Почему → Следствие → На экране: Из-за потока записей в основную БД реплика отстаёт на несколько секунд → Если читать только что сохранённые данные с реплики, их там ещё нет → Только что купленный предмет не виден, на торговой площадке старые цены, баги с двойной выдачей - Симптомы: Съеденные действия / роллбэк / Факторы: Задержка - У кого: Только одна функция / Когда: При наплыве игроков, Вечерний пик - Основной ответственный: Команда инфраструктуры · Инфраструктура БД / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Только что записанные данные читать из основной БД, проверку факта выдачи и саму выдачу делать в основной БД одной транзакцией (дубли отсекать уникальным ключом или условным UPDATE). - Команда инфраструктуры, задачи: Настроить алерт на отставание репликации, делать реплики не слабее основной БД, включить параллельную репликацию, массовые удаления выполнять мелкими порциями, контролировать долгие агрегирующие запросы на репликах. - На графике: Растёт вслед за онлайном и нагрузкой (отставание репликации (с)) - Где смотреть: В MySQL смотреть на реплике Seconds_Behind_Source в SHOW REPLICA STATUS (в версиях до 8.0.22 SHOW SLAVE STATUS). В PostgreSQL смотреть write_lag, flush_lag и replay_lag в pg_stat_replication на основном сервере, в RDS смотреть ReplicaLag - Подтверждает: В моменты жалоб «не видно» отставание составляет несколько секунд и больше, а когда оно рассасывается, всё отображается нормально. Отставание растёт во время потока записей, массовых удалений или долгих агрегирующих запросов на реплике - Опровергает: Если отставание около 0, а данных всё равно не видно, дело в кэше или синхронизации на игровом сервере - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Отставание бывает и без большого потока записей. Одно массовое удаление, которое на основной БД заняло 10 минут, повторно выполняется на реплике и на столько же её задерживает, а долгие агрегирующие запросы на реплике тоже мешают ей догонять. - Источники: - [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 один поток применяет их по порядку) - [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 · write_lag, flush_lag и replay_lag в pg_stat_replication: время от записи WAL на основном сервере до подтверждения от реплики, что она записала его, сбросила на диск и применила - [Amazon CloudWatch metrics for Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-metrics.html) · AWS · ReplicaLag: на сколько секунд реплика для чтения отстаёт от исходной БД #### db-checkpoint · Контрольные точки и сброс журнала · Checkpoint / log flush stalls В моменты, когда БД периодически сбрасывает накопленные в памяти изменения на диск, запросы замедляются. - Почему → Следствие → На экране: Изменения накапливаются и периодически записываются на диск → В этот момент диск занят, и запросы задерживаются → Сохранения и загрузки периодически замедляются - Симптомы: Задержка ввода, Микрофризы / Факторы: Задержка - У кого: Весь сервер, Только одна функция / Когда: С постоянным периодом - Основной ответственный: Команда инфраструктуры · Инфраструктура БД - Команда инфраструктуры, задачи: Растягивать контрольные точки мелкими равномерными порциями, делать журнал транзакций (redo-лог, WAL) с запасом, ставить быстрые диски. - На графике: Всплески с постоянным периодом (задержка запросов к БД, объём записи на диск) - Где смотреть: Для PostgreSQL смотреть в логе log_checkpoints (в последних версиях включён по умолчанию) время контрольных точек и число записанных буферов, число контрольных точек (с версии 17 num_timed и num_requested в pg_stat_checkpointer, в 16 и ниже checkpoints_timed и checkpoints_req в pg_stat_bgwriter) и предупреждения checkpoint_warning. Для MySQL смотреть в секции LOG вывода SHOW ENGINE INNODB STATUS разницу между Log sequence number и Last checkpoint at. Накладывать на объём и задержку записи на диск сервера - Подтверждает: Всплески задержки запросов совпадают с контрольными точками, и в эти моменты подскакивают объём и задержка записи на диск. Если в PostgreSQL контрольных точек по запросу (num_requested) намного больше, чем по времени (num_timed), значит WAL часто достигает max_wal_size и контрольные точки наступают раньше срока - Опровергает: Если всплески идут с периодом, не связанным с контрольными точками, это бэкап или пакетные задания (dk-backup, db-batch) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Если журнал транзакций, куда записываются изменения (redo-лог в MySQL, WAL в PostgreSQL), слишком мал, то при каждом заполнении журнала БД в спешке делает контрольную точку, и производительность записи ненадолго сильно падает. - Источники: - [WAL Configuration (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-configuration.html) · PostgreSQL · Контрольная точка выполняется по умолчанию каждые 5 минут или каждый 1 GB WAL (max_wal_size) и стоит дорого, потому что записывает все грязные страницы. checkpoint_completion_target растягивает запись, чтобы избежать всплеска ввода-вывода. Если интервал между контрольными точками короче 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 · num_timed (контрольные точки по расписанию) и num_requested (запрошенные контрольные точки) в pg_stat_checkpointer - [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 включительно checkpoints_timed и checkpoints_req в pg_stat_bgwriter - [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 После перезапуска БД кэш в памяти пуст, и какое-то время все запросы читают данные с диска. - Почему → Следствие → На экране: БД перезапускается на техработах → Часто используемых данных нет в памяти, они читаются с диска → Сразу после техработ какое-то время медленно проходят вход и загрузка - Симптомы: Ошибка входа / бесконечная загрузка, Задержка ввода / Факторы: Задержка - У кого: Весь сервер / Когда: Сразу после входа или техработ - Основной ответственный: Команда инфраструктуры · Инфраструктура БД / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Открывать сервер постепенно (через очередь на вход поэтапно увеличивать число входящих игроков). - Команда инфраструктуры, задачи: Прогревать кэш после перезапуска (проверить настройки сохранения и восстановления буферного пула), у БД, восстановленной из снапшота, заранее прочитать и диск. - На графике: Всплеск сразу после входа или техработ (чтение с диска, доля попаданий в буферный кэш) - Где смотреть: В MySQL смотреть отношение Innodb_buffer_pool_reads (сколько раз данных не было в буферном пуле и пришлось читать с диска) к Innodb_buffer_pool_read_requests и ход прогрева в Innodb_buffer_pool_load_status. В PostgreSQL смотреть blks_read и blks_hit в pg_stat_database. Смотреть и число чтений с диска на сервере БД - Подтверждает: Сразу после перезапуска чтение с диска подскакивает, доля попаданий низкая и со временем восстанавливается, и на этом отрезке медленно проходят вход и загрузка - Опровергает: Если доля попаданий обычная, а после техработ всё медленно, это наплыв входов и N+1 (db-login-storm) или пул соединений (db-pool) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: MySQL при выключении сохраняет список страниц буферного пула, а при запуске в фоне читает их заново, но на полное заполнение нужно время. Если в облаке БД восстановлена из снапшота (копии диска), то и сам диск медленно отдаёт каждый блок при первом чтении, и прогрев затягивается ещё сильнее. - Реальные инциденты: 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 · blks_read (число блоков, прочитанных с диска) и blks_hit (число блоков, найденных в буферном кэше) в pg_stat_database #### db-login-storm · Наплыв входов и запросы N+1 · Login storm, N+1 queries Если для загрузки одного персонажа нужны десятки отдельных запросов, одновременный вход десятков тысяч игроков превращается в миллионы запросов. - Почему → Следствие → На экране: При загрузке персонажа предметы, умения и квесты запрашиваются по отдельности → Сразу после техработ одновременные входы резко увеличивают число запросов → Бесконечная загрузка при входе, застревают даже сохранения тех, кто уже играет - Симптомы: Ошибка входа / бесконечная загрузка, Задержка ввода / Факторы: Остановка, Задержка - У кого: Весь сервер / Когда: Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Запрашивать данные одним пакетом, ввести очередь на вход, кэшировать, проверить, сколько запросов порождает ленивая загрузка в ORM. - Команда инфраструктуры, задачи: Составлять рейтинг запросов по числу вызовов и передавать его команде разработки, мониторить число запросов и соединений в часы входа сразу после техработ. - На графике: Всплеск сразу после входа или техработ (запросов к БД в секунду, число входов) - Где смотреть: Накладывать число входов сразу после техработ на число запросов к БД в секунду (в MySQL прирост Questions) и считать число запросов на один вход. Запросы с наибольшим числом вызовов выбирать по COUNT_STAR в events_statements_summary_by_digest в MySQL и по calls в pg_stat_statements в PostgreSQL - Подтверждает: На один вход приходятся десятки запросов, а в топе одинаковые короткие запросы по одному ID персонажа. Если после патча число запросов на вход выросло, разбор начинают с этого патча - Опровергает: Если запросов на вход мало, но каждый медленный, это холодный кэш (db-cold-cache) или индекс (db-no-index) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Ленивая загрузка в ORM (библиотеке, которая сама строит запросы к БД) порождает такие запросы незаметно даже для разработчиков. На сервере разработки всего несколько персонажей, и проблемы не видно, а впервые она проявляется при одновременном входе на живых серверах. - Источники: - [Efficient Querying](https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying) · .NET · Ленивая загрузка в ORM порождает проблему 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 → В определённые часы срываются обмены и сохранения, медленно идёт загрузка - Симптомы: Задержка ввода, Съеденные действия / роллбэк / Факторы: Задержка, Остановка - У кого: Только одна функция, Весь сервер / Когда: С постоянным периодом - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Дробить работу на мелкие порции, агрегацию выполнять на реплике. - Команда инфраструктуры, задачи: Выделить реплику для агрегации, планировать пакетные задания на часы низкой нагрузки, следить за эскалацией блокировок и ожиданиями gap-блокировок. - На графике: Всплески с постоянным периодом (задержка запросов к БД, ожидания блокировок) - Где смотреть: Найти долгие запросы, которые выполнялись в момент лагов. Для MySQL смотреть slow query log, для PostgreSQL query_start и query в pg_stat_activity и сверять с метриками ожидания блокировок за то же время и с расписанием пакетных заданий (cron, планировщик событий БД). В SQL Server записывать эскалацию блокировок расширенным событием lock_escalation - Подтверждает: Каждый раз в одно и то же время идут массовые UPDATE, DELETE или агрегирующие запросы, и в это время вместе растут ожидания блокировок и утилизация диска - Опровергает: Если в это время долгих запросов нет, это контрольная точка (db-checkpoint) или бэкап сервера (dk-backup) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: SQL Server, когда один оператор захватывает больше примерно 5 000 блокировок строк, заменяет их блокировкой таблицы (эскалация блокировок). В этот момент останавливаются все запросы к этой таблице. MySQL в настройках по умолчанию при изменении по условию диапазона тоже блокирует промежутки между строками (gap-блокировка) и не даёт добавлять новые строки. - Источники: - [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 · Если один оператор захватывает 5 000 и больше блокировок в одной таблице (или индексе), происходит эскалация блокировок, её записывает расширенное событие lock_escalation - [InnoDB Locking](https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html) · MySQL · При уровне изоляции InnoDB по умолчанию REPEATABLE READ поиск и сканирование используют next-key-блокировки, и gap-блокировка не даёт добавлять новые строки в этот промежуток - [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 · Переключение БД на резерв · Database failover Пока после отказа основной БД идёт переключение на резервную, запись невозможна, а последние данные, которые не успели реплицироваться, могут пропасть. - Почему → Следствие → На экране: Из-за отказа основной БД резервная повышается до основной → Во время переключения запись невозможна от нескольких секунд до нескольких минут, при асинхронной репликации возможна потеря нереплицированных данных → Ненадолго не проходят все сохранения, роллбэк предметов и опыта - Симптомы: Съеденные действия / роллбэк, Фриз, Дисконнект, Ошибка входа / бесконечная загрузка / Факторы: Остановка, Потери - У кого: Весь сервер / Когда: Изредка, случайно - Основной ответственный: Команда инфраструктуры · Инфраструктура БД / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Делать сохранения, которые можно безопасно повторить, настроить быстрый сброс оборванных соединений и переподключение по новому адресу (пул соединений, кэш DNS), проверять переподключение на учениях по переключению. - Команда инфраструктуры, задачи: Использовать синхронную или полусинхронную репликацию (ценой роста задержки записи), проводить учения по переключению, отслеживать время переключения и отставание репликации. - Цифры для ориентира: Автоматическое переключение управляемой БД обычно занимает от десятков секунд до 2 минут. При асинхронной репликации можно потерять последние сохранения за время, равное отставанию репликации (от менее чем 1 секунды до нескольких секунд). - На графике: Массовый обрыв соединений (число соединений с БД, число ошибок записи) - Где смотреть: Поставить на один график записи о переключении на стороне БД (в RDS события RDS-EVENT-0013 начало переключения и RDS-EVENT-0049 окончание, в самостоятельно управляемой БД лог повышения реплики) и число соединений с БД и ошибок соединения на игровых серверах. При асинхронной репликации смотреть и отставание репликации перед отказом (ReplicaLag в RDS, replay_lag в pg_stat_replication в PostgreSQL) - Подтверждает: Сбои сохранения сосредоточены в одном отрезке, и он совпадает с интервалом между началом и концом переключения. Потерянный отрезок прогресса примерно равен отставанию репликации перед отказом. Игровой сервер, у которого ошибки продолжаются и после переключения, всё ещё использует соединения со старым адресом - Опровергает: Обрывы соединений в моменты, когда записей о переключении нет, указывают на сеть или перегрузку БД - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Реальные инциденты: 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 секунд, после него нужно заново установить соединения, TTL кэша DNS в JVM рекомендуется не больше 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 · При асинхронной репликации после отказа основной БД закоммиченных транзакций может не оказаться на реплике. Полусинхронная репликация сокращает этот риск, дожидаясь подтверждения получения от одной реплики, но задержка растёт - [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: на сколько секунд реплика для чтения отстаёт от исходной БД - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · replay_lag в pg_stat_replication: время от записи WAL на основном сервере до подтверждения от реплики, что она его применила #### db-save-interval · Потеря прогресса из-за редких сохранений · Periodic save window Если ради снижения нагрузки сохранять прогресс раз в несколько минут, то при падении сервера в промежутке прогресс пропадает. - Почему → Следствие → На экране: Состояние персонажа сохраняется раз в несколько минут → В промежутке сервер падает или происходит сбой → После перезахода персонаж в состоянии нескольких минут назад (роллбэк) - Симптомы: Съеденные действия / роллбэк / Факторы: Потери - У кого: Весь сервер, Одна локация или канал / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Важные события (обмен, редкая добыча) сохранять сразу, вести журнал изменений. - Команда инфраструктуры, задачи: Проверить, хватит ли у БД запаса IOPS и CPU на рост записи при сокращении интервала сохранения. - На графике: Массовый обрыв соединений (число подключений, число жалоб на роллбэк) - Где смотреть: Сопоставить время падения или сбоя и время последнего сохранения персонажей, на которых пожаловались из-за роллбэка (лог сохранений игрового сервера или столбец времени изменения в БД) - Подтверждает: Момент, к которому вернулся персонаж, совпадает с последним сохранением перед падением, а потерянное время меньше интервала сохранения - Опровергает: Если в логе игрового сервера сохранение отмечено как завершённое, а прогресс всё равно вернулся назад, это потеря данных при переключении БД на резерв (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 · Cache stampede / thundering herd Когда кэш популярных данных истекает одновременно, тысячи запросов разом идут в БД. - Почему → Следствие → На экране: Популярные данные в Redis или другом кэше истекают одновременно → Запросы, которые заново строят те же данные, разом обрушиваются на БД → БД перегружена, и одна функция за другой начинает тормозить или перестаёт отвечать - Симптомы: Задержка ввода, Фриз, Ошибка входа / бесконечная загрузка / Факторы: Остановка, Задержка - У кого: Весь сервер / Когда: С постоянным периодом, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Разносить время истечения случайным образом, обновлять данные одним запросом, а остальным отдавать старое значение. - Команда инфраструктуры, задачи: Настроить для Redis реплики и автоматическое переключение, чтобы при перезапуске или сбое кэш не опустел целиком, проверить, выдержит ли БД пустой кэш. - На графике: Всплески с постоянным периодом (доля попаданий в кэш, запросов к БД в секунду) - Где смотреть: Накладывать keyspace_hits и keyspace_misses (доля попаданий), expired_keys и признак перезапуска (uptime_in_seconds) из Redis INFO на число запросов к БД в секунду и считать, сколько одинаковых запросов одновременно выполняется в БД в этот момент (MySQL SHOW PROCESSLIST, PostgreSQL pg_stat_activity) - Подтверждает: В момент резкого скачка промахов кэша вместе подскакивает число запросов к БД, и большинство одновременных запросов одинаковые и читают одни и те же данные. Совпадает с периодом истечения популярного ключа или с перезапуском Redis - Опровергает: Если промахов кэша столько же, сколько обычно, а растут только запросы к БД, это наплыв входов (db-login-storm) или пакетное задание (db-batch) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: То же самое бывает, когда Redis перезапускается или из-за сбоя кэш пустеет целиком. Особенно опасно, если в расчёте на кэш БД сделали маломощной. - Источники: - [Scaling Memcache at Facebook (NSDI '13)](https://www.usenix.org/conference/nsdi13/technical-sessions/presentation/nishtala) · USENIX · Когда инвалидируется часто используемый ключ, множество чтений устремляется в БД (thundering herd). Это предотвращают арендой (lease, обновляет только один клиент) и выдачей старого значения, кластер с пустым кэшем прогревают отдельно - [Optimal Probabilistic Cache Stampede Prevention](https://www.vldb.org/pvldb/vol8/p886-vattani.pdf) · VLDB Endowment · Когда истекает популярный элемент, его одновременно пересоздают многие запросы (cache stampede). Это предотвращают вероятностным досрочным обновлением до истечения - [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 Если одна транзакция долго остаётся открытой, она продолжает держать блокировки, а БД не может очистить (purge) старые версии данных, и всё постепенно замедляется. - Почему → Следствие → На экране: Транзакция остаётся открытой, пока ждёт ответа другого сервера, или на основной БД в рабочее время идёт долгий агрегирующий запрос → Захваченные блокировки не освобождаются, старые версии данных, которые нужно удалить, продолжают копиться → Таймауты в функциях, которые обращаются к этой строке, за несколько часов в целом замедляются сохранения и чтение данных - Симптомы: Задержка ввода, Съеденные действия / роллбэк / Факторы: Задержка, Остановка - У кого: Только одна функция, Весь сервер / Когда: Чем дольше без перезапуска, Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Не ждать внутри транзакции сетевых вызовов и ввода пользователя, агрегирующие запросы выполнять на реплике. - Команда инфраструктуры, задачи: Настроить алерт на долго открытые транзакции и принудительно их завершать, выделить реплику для агрегации, следить за ростом undo-лога и мёртвых строк. - На графике: Плавный рост (длина undo-лога (History list length), число мёртвых строк) - Где смотреть: В MySQL искать самую старую транзакцию по trx_started в INFORMATION_SCHEMA.INNODB_TRX и смотреть History list length (объём ещё не очищенного undo-лога) в секции TRANSACTIONS вывода SHOW ENGINE INNODB STATUS. В PostgreSQL смотреть xact_start в pg_stat_activity и сессии в состоянии idle in transaction, а также n_dead_tup в pg_stat_user_tables - Подтверждает: Есть транзакция возрастом от нескольких минут до нескольких часов, всё это время History list length или n_dead_tup растут, а после завершения этой транзакции очистка (purge, VACUUM) их снижает - Опровергает: Если старых транзакций нет, а в целом всё медленно, дело в контрольных точках (db-checkpoint) или в диске - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Чтобы читающие видели данные в состоянии до изменения, БД хранит старые версии (MVCC). Удалить эти записи можно только после завершения самой старой транзакции, поэтому если одна транзакция открыта несколько часов, в MySQL копится undo-лог, а в PostgreSQL мёртвые строки (dead tuple), которые не может убрать VACUUM. В SQL Server журнал транзакций не сокращается и может заполнить диск. - Источники: - [InnoDB Multi-Versioning](https://dev.mysql.com/doc/refman/8.4/en/innodb-multi-versioning.html) · MySQL · Пока остаётся транзакция, которой могут понадобиться старые версии, update undo-лог нельзя удалить и rollback-сегмент растёт. Рекомендуется часто коммитить даже транзакции, которые только читают - [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 · Purge очищает список undo-логов закоммиченных транзакций (history list), отставание показывает History list length в секции TRANSACTIONS вывода SHOW ENGINE INNODB STATUS - [The Cumulative Statistics System (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/monitoring-stats.html) · PostgreSQL · xact_start (время начала транзакции) и state (idle in transaction) в pg_stat_activity, n_dead_tup (оценка числа мёртвых строк) в pg_stat_user_tables #### db-redis-block · Медленные команды Redis · Redis blocking commands (single-threaded) Redis обрабатывает команды по одной, поэтому одна медленная команда блокирует все запросы за ней. - Почему → Следствие → На экране: В рабочее время выполняется полный перебор через KEYS, рейтинг или список из миллионов элементов читается или удаляется целиком → Пока команда не закончится, все остальные запросы ждут (от десятков ms до нескольких секунд) → Одновременно подвисают функции, которые используют сессии, рейтинги и кэш, задерживается вход - Симптомы: Фриз, Задержка ввода, Ошибка входа / бесконечная загрузка / Факторы: Остановка, Задержка - У кого: Весь сервер, Только одна функция / Когда: Изредка, случайно, С постоянным периодом - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Использовать SCAN вместо KEYS, дробить большие ключи, удалять через UNLINK (фоновое удаление), разносить время истечения ключей, которое приходится на одну и ту же секунду. - Команда инфраструктуры, задачи: Следить за журналом медленных команд (SLOWLOG), запретить опасные команды вроде KEYS на боевых серверах, регулярно проверять большие ключи, отключить THP и держать запас памяти для fork, сохранять RDB и AOF на реплике. - Цифры для ориентира: Обычная команда выполняется меньше чем за 1 ms. Операция над миллионами элементов за раз может занять от сотен ms до нескольких секунд. - На графике: Случайные всплески (задержка ответа Redis, число медленных команд) - Где смотреть: Через SLOWLOG GET смотреть команды дольше slowlog-log-slower-than, включить монитор задержек (по умолчанию выключен) через CONFIG SET latency-monitor-threshold и смотреть задержки по событиям вроде fork и expire-cycle в LATENCY LATEST и LATENCY DOCTOR. Время fork и большие ключи проверять по latest_fork_usec в INFO и через redis-cli --bigkeys - Подтверждает: В момент подвисания в SLOWLOG есть KEYS или команды, которые целиком обрабатывают большой ключ, либо в LATENCY в то же время записаны события fork или expire-cycle на десятки ms и больше - Опровергает: Если SLOWLOG и LATENCY пусты, а медленно только со стороны игрового сервера, дело в сети или в ожидании внутри игрового сервера (SLOWLOG измеряет только время выполнения команды без обмена данными с клиентом) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Redis останавливается и в момент, когда для создания файла сохранения (снапшота RDB) или перезаписи AOF копирует процесс (fork). На современных серверах это около 10 ms на 1 GB памяти, то есть для 30 GB около 300 ms. Если включены большие страницы (THP), после fork каждая запись копирует большую страницу целиком (copy-on-write), и паузы и потребление памяти сильно растут, поэтому THP обычно отключают и держат большой запас памяти. Когда в одну и ту же секунду истекает очень много ключей, Redis тоже ненадолго останавливается, чтобы их удалить. - Источники: - [Diagnosing latency issues](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency/) · Redis · Запросы по очереди обрабатывает один поток, и медленная команда блокирует всё за ней, SCAN вместо KEYS, fork по измерениям на физических серверах и современных ВМ занимает около 9–13 ms на 1 GB, THP из-за копирования после fork резко увеличивает задержки и память, массовое истечение ключей в одну секунду вызывает остановку - [KEYS](https://redis.io/docs/latest/commands/keys/) · Redis · В боевой среде использовать с крайней осторожностью, на большой базе может убить производительность (на бюджетном ноутбуке 40 ms на 1 000 000 ключей) - [UNLINK](https://redis.io/docs/latest/commands/unlink/) · Redis · Асинхронное удаление: ключ сразу отсоединяется, а память освобождается в другом потоке - [SLOWLOG](https://redis.io/docs/latest/commands/slowlog/) · Redis · Журнал медленных команд записывает команды дольше slowlog-log-slower-than, время выполнения не включает ввод-вывод при обмене с клиентом - [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) Код не менялся, но если БД меняет способ выполнения того же запроса (план выполнения), вчерашний запрос на 2 ms сегодня занимает сотни ms. - Почему → Следствие → На экране: Автоматическое обновление статистики, перезапуск БД или изменение распределения данных заставляют БД построить план заново → Выбирается план без индекса, тот же запрос становится медленнее в десятки или сотни раз и надолго занимает соединения → Деплоя не было, но загрузка в какой-то функции внезапно замедляется, и ждут даже другие запросы - Симптомы: Задержка ввода, Ошибка входа / бесконечная загрузка / Факторы: Задержка, Остановка - У кого: Только одна функция, Весь сервер / Когда: Изредка, случайно - Основной ответственный: Команда инфраструктуры · Инфраструктура БД / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Запросы, у которых число результатов сильно зависит от значения параметра, разделять или рассмотреть подсказки плана, проектировать запросы так, чтобы они гарантированно использовали индекс. - Команда инфраструктуры, задачи: Следить за медленными запросами и записями планов выполнения, фиксировать хорошие планы (хранилище запросов SQL Server и др.), управлять временем обновления статистики. - На графике: Ступенька вверх с определённого момента (среднее время выполнения по запросам) - Где смотреть: Периодически собирать среднее время запросов одного вида и смотреть динамику. В MySQL это AVG_TIMER_WAIT в events_statements_summary_by_digest, в PostgreSQL mean_exec_time в pg_stat_statements (в 12 и ниже mean_time). Планы выполнения до и после замедления сравнивать через EXPLAIN или auto_explain в PostgreSQL, в SQL Server через представление «Регрессированные запросы» (Regressed Queries) хранилища запросов - Подтверждает: В отсутствие деплоя среднее время одного запроса ступенькой вырастает в несколько десятков раз, этот момент совпадает с обновлением статистики или перезапуском БД, и план выполнения изменился - Опровергает: Если план выполнения тот же, а запрос замедлился, дело в росте данных, ожидании блокировок (db-hot-row) или диске - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: SQL Server повторно использует план, построенный под первое пришедшее значение (прослушивание параметров, parameter sniffing). Если план, построенный для нового персонажа с несколькими предметами, применяется к старому персонажу с десятками тысяч предметов, запрос сильно замедляется, и обратная ситуация тоже частая. Когда перезапуск очищает планы, всё приходит в норму, а потом может снова испортиться. - Источники: - [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 · Если данные распределены неравномерно, один закэшированный план подходит не для всех значений параметров - [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) Если во время работы сервиса добавить в таблицу столбец или индекс, из-за одной кратковременной блокировки могут встать все запросы к этой таблице. - Почему → Следствие → На экране: Хотфиксом в рабочую таблицу добавляется столбец или индекс → Изменение схемы ждёт ранее открытую длинную транзакцию, а все следующие запросы ждут изменение схемы → Функции, которые используют эту таблицу (инвентарь, почта и т. п.), полностью перестают работать, запросы завершаются по таймауту - Симптомы: Задержка ввода, Съеденные действия / роллбэк, Ошибка входа / бесконечная загрузка / Факторы: Остановка - У кого: Только одна функция, Весь сервер / Когда: Изредка, случайно, Сразу после входа или техработ - Основной ответственный: Команда инфраструктуры · Инфраструктура БД / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Хотфиксы с изменением схемы согласовывать по срокам с инфраструктурой БД, сначала деплоить код, который работает и без нового столбца. - Команда инфраструктуры, задачи: Ставить короткий лимит ожидания блокировки и при сбое повторять, запускать, когда нет длинных транзакций, использовать инструменты онлайн-изменения схемы, большие таблицы менять во время техработ. - На графике: Ступенька вверх с определённого момента (число сессий в ожидании блокировки, задержка запросов к этой таблице) - Где смотреть: В MySQL считать сессии с State Waiting for table metadata lock в SHOW PROCESSLIST и искать блокирующую сессию (blocking_pid) через sys.schema_table_lock_waits. В PostgreSQL смотреть запросы, у которых granted равно false, и AccessExclusiveLock в pg_locks и искать блокирующую сессию через pg_blocking_pids() - Подтверждает: С момента запуска изменения схемы все запросы к этой таблице копятся в ожидании блокировки, а в начале очереди стоит незавершённая транзакция или оператор изменения схемы - Опровергает: Если ожидания сосредоточены только на определённых строках, а остальные строки той же таблицы обрабатываются нормально, это горячая строка (db-hot-row) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: При изменении схемы MySQL ненадолго берёт блокировку метаданных, а PostgreSQL самую сильную блокировку таблицы. Само изменение мгновенное, но если перед ним висит одна незавершённая транзакция, все запросы за ним встают в очередь. - Источники: - [Online DDL Performance and Concurrency](https://dev.mysql.com/doc/refman/8.4/en/innodb-online-ddl-performance.html) · MySQL · Даже онлайн-DDL в конце ненадолго требует эксклюзивную блокировку метаданных. Если есть длинная транзакция, 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 Если поставить между клиентом и игровым сервером промежуточный сервер, каждый проход через него добавляет время обработки, а сам он становится единой точкой отказа. - Почему → Следствие → На экране: Схема клиент ↔ шлюз ↔ игровой сервер → Промежуточный сервер добавляет обработку и ожидание, при его перегрузке страдают все → Пинг растёт у всех, при отказе шлюза дисконнект у всех игроков, которые через него подключены - Симптомы: Задержка ввода, Дисконнект / Факторы: Задержка, Остановка - У кого: Весь сервер / Когда: При наплыве игроков, Всегда - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: заложить возможность добавлять шлюзы и восстановление сессии, чтобы при падении шлюза персонаж продолжал игру после переподключения к другому шлюзу. Клиент: автоматически переподключаться при обрыве связи со шлюзом. - Команда инфраструктуры, задачи: Масштабировать шлюзы горизонтально (добавлять машины), мониторить CPU, число соединений и задержку обработки на каждом шлюзе. - Цифры для ориентира: Внутри одного ЦОД один проход обычно занимает меньше 1 ms. При перегрузке шлюза это время вырастает до десятков и сотен ms. - На графике: Растёт вслед за онлайном и нагрузкой (задержка обработки на шлюзе, CPU и число соединений шлюза) - Где смотреть: Смотреть CPU и число соединений шлюза, Recv-Q его сокетов (ss, netstat) и разницу задержки до шлюза и после него. Для вызовов HTTP и gRPC через service mesh сравнить стандартную метрику Istio istio_request_duration_milliseconds на стороне отправителя (reporter=source) и получателя (reporter=destination) - Подтверждает: Время обработки на игровом сервере не меняется, растёт только задержка на участке шлюза, и в это же время CPU шлюза упирается в потолок или копится Recv-Q - Опровергает: Если маршрут в обход этого шлюза (прямое подключение, другой шлюз) тормозит так же, проблема в линии связи или на игровом сервере - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: С service mesh (Istio и т. п.) добавляется ещё одно звено: sidecar-прокси (Envoy), который работает рядом с каждым сервером. Запрос между сервисами проходит сначала через sidecar отправителя, потом через sidecar получателя, и чем больше функций добавлено в прокси (например, сбор логов и метрик), тем больше время обработки и ожидания. - Реальные инциденты: 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 клиент подключается к одному из 4 входных серверов (REP) с публичным адресом и через него обменивается данными со стоящими за ним серверами симуляции (хабами) - [Designs, Lessons and Advice from Building Large Distributed Systems](https://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf) · Google · Пленарный доклад Jeff Dean на LADIS 2009. Путь туда и обратно внутри одного ЦОД около 0,5 ms - [Site Reliability Engineering, Chapter 22: Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/) · Google · При перегрузке очередь растёт, и ожидание становится в несколько раз дольше обработки (обработка 100 ms, очередь в 10 раз больше числа потоков: 1,1 с) - [Performance and Scalability](https://istio.io/latest/docs/ops/deployment/performance-and-scalability/) · Istio · В режиме sidecar запрос проходит по очереди через sidecar-прокси отправителя и получателя. Чем больше функций, тем длиннее путь обработки внутри прокси, а сбор телеметрии увеличивает ожидание следующего запроса - [What is Envoy](https://www.envoyproxy.io/docs/envoy/latest/intro/what_is_envoy) · Envoy · Envoy работает отдельным процессом рядом с каждым сервером приложений, и приложение обменивается данными через Envoy на localhost - [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 Когда один сервис тормозит, вызывающие его серверы зависают в ожидании ответа, и останавливаются даже функции, которые с ним не связаны. - Почему → Следствие → На экране: Тормозит один сервис, например БД или авторизация → Потоки и соединения вызывающих серверов заняты ожиданием ответа, а повторные попытки неудачных запросов добавляют нагрузку → Тормозят или останавливаются даже функции, которые кажутся никак не связанными - Симптомы: Фриз, Задержка ввода, Ошибка входа / бесконечная загрузка / Факторы: Остановка - У кого: Весь сервер / Когда: При наплыве игроков, Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Сетевая инфраструктура - Команда разработки, задачи: Ставить таймауты на все вызовы, использовать circuit breaker (предохранитель для вызовов) и изоляцию функций друг от друга (bulkhead), повторять с растущим интервалом и ограничением числа попыток, отделить ответы на health check от тяжёлой работы. - Команда инфраструктуры, задачи: Дать запас по числу неудач и интервалу health check балансировщика, чтобы ненадолго притормозивший сервер не выводился сразу, и ограничить число серверов, выводимых одновременно. - На графике: Упор в лимит (плато) (время ответа и доля ошибок по сервисам, число занятых потоков и соединений) - Где смотреть: Вывести на один экран с общей шкалой времени время ответа, долю ошибок и число повторов по сервисам и найти место, которое замедлилось первым. За балансировщиком смотреть время ответа целевых серверов (в AWS ALB это TargetResponseTime), число ответов 5xx от них (HTTPCode_Target_5XX_Count) и число целевых серверов, выведенных как неисправные (UnHealthyHostCount) - Подтверждает: Сначала растёт задержка одного сервиса, затем у вызывающих его сторон число занятых потоков и соединений упирается в лимит, ошибки переходят на другие сервисы, а вместе с ними растут число повторов и число выведенных целевых серверов - Опровергает: Если несколько сервисов замедлились в один и тот же момент, сначала проверить сбой общего ресурса (БД, сеть, хост) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Health check (проверка, жив ли сервер) тоже раскручивает каскад. Если занятый сервер отвечает на проверку с опозданием, балансировщик выводит исправный сервер из ротации, его трафик уходит на оставшиеся, и следующий сервер тоже начинает опаздывать с ответами. - Реальные инциденты: 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 · Перегруженный сервер не проходит health check и выводится, нагрузка ложится на оставшиеся, а повторы её усиливают. Рекомендуются лимит повторов, экспоненциальная задержка повтора со случайным разбросом и дедлайны - [Circuit Breaker Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) · Microsoft Azure · Запросы, которые висят до таймаута, держат потоки и соединения с БД, и из-за этого отказывают даже не связанные функции. Если за заданное время накопилось слишком много неудач, вызовы сразу отклоняются - [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 повторах на каждом уровне нагрузка на БД вырастает в 243 раза. Повторять нужно только в одном месте и ограничивать повторы через token bucket - [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 Если отказывает сервер, который работает отдельно от игрового (чат, группы, аукцион), перестаёт работать только эта функция. - Почему → Следствие → На экране: Выделенный сервер функции тормозит или падает → Нет ответа только на запросы этой функции → Не работает чат, приглашение в группу остаётся без ответа, бесконечная загрузка торговой площадки (бои идут нормально) - Симптомы: Съеденные действия / роллбэк, Ошибка входа / бесконечная загрузка / Факторы: Остановка, Потери - У кого: Только одна функция / Когда: Изредка, случайно, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Проектировать так, чтобы игра продолжалась при сбое функции, показывать состояние каждой функции, не собирать несколько функций на одном центральном сервере. - Команда инфраструктуры, задачи: Настроить health check и алерты для каждого вспомогательного сервера, резервирование и автоматический перезапуск. - На графике: Массовый обрыв соединений (доля успешных запросов по функциям, число соединений и health check вспомогательных серверов) - Где смотреть: Смотреть health check, состояние процессов и число соединений каждого вспомогательного сервера (чат, группы, аукцион), долю успешных запросов и время ответа по функциям. За балансировщиком смотреть UnHealthyHostCount целевой группы - Подтверждает: Health check не проходит или число соединений резко падает только у сервера той функции, на которую жалуются, а тики игрового сервера и бои в норме - Опровергает: Если разом остановились несколько функций, причина скорее в центральном сервере, через который они все идут, или в каскадном отказе - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Если группы, гильдии, личные сообщения и переходы между серверами идут через один центральный сервер (сервер мира или сервер-менеджер), то при его замедлении сразу останавливаются несколько функций. - Источники: - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · 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. Проектировать так, чтобы при отказе зависимости ключевые функции продолжали работать на немного устаревших или резервных данных - [CloudWatch metrics for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cloudwatch-metrics.html) · AWS · UnHealthyHostCount: число целевых серверов, признанных неисправными по health check #### in-deploy · Деплой и перезапуск · Deploy / rolling restart Если при перезапуске сервера ради обновления не перенести соединения, у всех игроков на этом сервере будет дисконнект, а сохранения перед остановкой и переподключения придут разом. - Почему → Следствие → На экране: Серверы по очереди перезапускаются при деплое хотфикса → Сервер останавливается без переноса соединений на другой, и сохранения всех игроков с этого сервера разом идут в БД → Дисконнект без предупреждения, лавина переподключений - Симптомы: Дисконнект, Ошибка входа / бесконечная загрузка, Задержка ввода / Факторы: Остановка - У кого: Весь сервер, Одна локация или канал / Когда: Изредка, случайно, Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Сделать drain (закрыть только новые подключения и ждать, пока уйдут текущие игроки), переносить персонажей на другой сервер, растягивать сохранения перед остановкой, после перезапуска объявлять готовность только после загрузки кэша и прогрева JIT, при горячей перезагрузке заранее читать данные в отдельном потоке и подменять их разом между тиками. - Команда инфраструктуры, задачи: Настроить деплой так, чтобы серверы перезапускались по одному после drain, а перезапущенный сервер получал трафик только после подтверждения готовности (прогрев завершён), заранее объявлять время деплоя. - Цифры для ориентира: Если на одном сервере 5 000 игроков, за несколько секунд до остановки в БД приходят 5 000 сохранений. - На графике: Массовый обрыв соединений (число подключений по серверам, число записей в БД) - Где смотреть: Наложить журнал деплой-инструмента (время перезапуска каждого сервера) вертикальными линиями (аннотациями) на графики числа подключений, обрывов, записей в БД и запросов на вход - Подтверждает: Число подключений на серверах по очереди резко падает в моменты перезапуска, прямо перед этим подскакивают записи в БД, сразу после него запросы на вход - Опровергает: Если время обрывов не совпадает с журналом деплоя и перезапусков, причина в падении сервера или в сетевом оборудовании - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Первые несколько минут после запуска сервер тоже работает медленно. Кэш пуст, и запросы к БД идут лавиной, а серверы на 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) не пускает трафик, пока не установлены соединения, не загружены файлы и не прогрет кэш - [Edit target group attributes for your Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/edit-target-group-attributes.html) · AWS · После снятия целевого сервера с регистрации новые соединения на него не отправляются, а существующим даётся время завершиться (drain, по умолчанию 300 с) #### in-autoscale · Задержка автомасштабирования · Autoscaling lag При наплыве игроков серверы добавляются автоматически, но подготовка занимает несколько минут, и всё это время существующие серверы перегружены. - Почему → Следствие → На экране: Резкий рост подключений со стартом события → Несколько минут, пока новый сервер запустится и будет готов → Первые несколько минут после старта события слоумо и ошибки входа - Симптомы: Слоумо, Ошибка входа / бесконечная загрузка / Факторы: Остановка - У кого: Весь сервер / Когда: При наплыве игроков, Сразу после входа или техработ - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Распределять игроков по каналам (тех, кто уже сидит в переполненном канале, на новый сервер не перенести), сократить время старта и загрузки данных на новом сервере. - Команда инфраструктуры, задачи: Масштабировать заранее до события, держать прогретые резервные серверы, при сокращении выключать сервер только после ухода оставшихся игроков. - Цифры для ориентира: На обнаружение нагрузки уходит от 1 до нескольких минут (метрики усредняются за несколько минут), ещё несколько минут на запуск нового сервера, чтение игровых данных и заполнение кэша. - На графике: Всплеск сразу после входа или техработ (число инстансов, загрузка CPU, очередь на вход) - Где смотреть: Наложить журнал автомасштабирования (момент решения о масштабировании и момент ввода нового инстанса в работу) на графики загрузки CPU и числа подключений. В AWS смотреть метрики группы Auto Scaling (видны, только если их включить) GroupDesiredCapacity (целевое число), GroupPendingInstances (готовятся) и GroupInServiceInstances (в работе) - Подтверждает: Несколько минут после всплеска подключений растут только целевое число и число готовящихся инстансов, а CPU существующих серверов держится у лимита и отпускает с того момента, когда растёт число инстансов в работе - Опровергает: Если и после ввода новых инстансов всё тормозит, причина не связана с числом серверов (общий ресурс вроде БД, каскадный отказ) - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Автомасштабирование обычно применяют там, где новых игроков достаточно принять на новом сервере: авторизация, шлюзы, данжи. Проблемы бывают и при сокращении. Если ночью, когда игроков мало, выключать лишние серверы, не дожидаясь ухода оставшихся игроков, у этих игроков будет дисконнект. - Реальные инциденты: 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 · Для приложений с долгой загрузкой задержку масштабирования сокращают пулом заранее инициализированных инстансов (warm pool) #### 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 и нескольких секунд. - На графике: Плавный рост (смещение часов по серверам) - Где смотреть: Собрать с каждого сервера значения System time (разница между системными часами и временем NTP), Last offset и Ref time (момент, когда последний раз учтено измерение источника времени) из chronyc tracking и сравнить - Подтверждает: Смещение проблемного сервера отличается от остальных на сотни ms и больше или Ref time давно не обновлялось, и расхождения проверок бывают только при переходах на этот сервер и с него - Опровергает: Если смещение на всех серверах в пределах единиц ms, причина в расчёте времени в игре или в ошибке синхронизации часов на клиенте - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Разовый скачок часов одного сервера вперёд или назад разобран в слое ОС сервера, в причине «Скачок системных часов (шаговая коррекция NTP)». - Источники: - [RFC 5905: Network Time Protocol Version 4: Protocol and Algorithms Specification](https://www.rfc-editor.org/rfc/rfc5905) · IETF · NTP-клиент в быстрой локальной сети обычно держит точность в пределах сотен µs - [chrony – Frequently Asked Questions](https://chrony-project.org/faq.html) · chrony · Дрейф часов обычного компьютера меньше 100 ppm, у виртуальных машин может быть больше. После паузы и возобновления у виртуальной машины время сбивается, и может понадобиться шаговая коррекция - [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 · System time (разница между временем NTP и системными часами), Last offset (смещение, оценённое при последней коррекции), Ref time (момент, когда учтено последнее измерение источника времени) из chronyc tracking #### in-bots · Избыток макросов и ботов · Bots and macros Боты шлют запросы гораздо чаще людей и съедают производительность сервера. - Почему → Следствие → На экране: Массовое подключение ботов, которые без перерыва фармят, ходят и торгуют → Растут нагрузка на сервер и на БД → Тормозит отдельная локация для фарма или весь сервер (слоумо, задержка ввода) - Симптомы: Слоумо, Задержка ввода / Факторы: Остановка - У кого: Весь сервер, Одна локация или канал / Когда: Всегда, Вечерний пик - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Сетевая инфраструктура - Команда разработки, задачи: Обнаруживать ботов, ограничивать частоту запросов по аккаунтам и персонажам. - Команда инфраструктуры, задачи: Ограничить частоту подключений и запросов по IP (с запасом, потому что в компьютерных клубах и мобильных сетях много игроков сидят за одним IP), блокировать диапазоны адресов ботов на файрволе и WAF. - На графике: Высоко только у некоторых (запросы в секунду по аккаунтам и IP) - Где смотреть: Смотреть в логах игрового сервера распределение числа запросов в секунду по аккаунтам и персонажам и топ по этому числу. Если метрик в коде нет, смотреть число запросов по IP на файрволе и WAF - Подтверждает: Небольшое число аккаунтов или 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 · Если один IP делят несколько абонентов, блокировки и ограничения по IP задевают и других пользователей #### in-external · Зависимость от внешних сервисов · External dependencies (auth, billing, platform) Если тормозит или останавливается внешний сервис (вход через платформу, оплата, подтверждение личности), всё застревает на этом этапе. - Почему → Следствие → На экране: Сбой или задержка внешнего сервиса авторизации или оплаты → На этом этапе идёт ожидание ответа → Не удаётся войти, платёж не проходит. Те, кто уже в игре, играют нормально - Симптомы: Ошибка входа / бесконечная загрузка, Съеденные действия / роллбэк / Факторы: Остановка - У кого: Весь сервер, Только одна функция / Когда: Сразу после входа или техработ, При определённом действии - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Ставить таймауты на внешние вызовы и показывать понятное сообщение, кэшировать результат авторизации, предусмотреть повтор платежа и порядок компенсации. - Внешние стороны, задачи: Запросить у провайдеров авторизации, оплаты и платформы подтверждение сбоя и восстановление, сообщить игрокам, что сбой на стороне внешнего сервиса. - На графике: Ступенька вверх с определённого момента (время ответа и доля ошибок внешних вызовов, число успешных входов) - Где смотреть: Смотреть время ответа, долю ошибок и число таймаутов по каждому внешнему вызову (вход через платформу, оплата, подтверждение личности) и страницу статуса провайдера - Подтверждает: С момента, когда пошли неудачные входы и платежи, ошибки и таймауты одного внешнего вызова поднимаются ступенькой и держатся, а на странице статуса провайдера на то же время отмечен сбой - Опровергает: Если внешние вызовы в норме, а вход не работает, причина в самом сервере авторизации (исчерпание пула потоков, БД) или в очереди подключений ОС (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) Если игрока отправили на сервер в дальнем регионе вместо ближнего, у него одного пинг всегда высокий, даже если с подключением всё в порядке. - Почему → Следствие → На экране: Ошибки в данных GeoIP, VPN, выбор сервера для всей группы по среднему пингу участников, правило расширения поиска на дальние регионы при нехватке игроков, выбор по местоположению DNS-резолвера → Подключение к серверу в регионе за океаном, хотя есть ближний регион → В игре с серверами в нескольких регионах только у одного игрока (или одной группы) постоянно высокий пинг, задержка ввода, откидывание назад и съеденные умения - Симптомы: Задержка ввода, Откидывание назад, Съеденные действия / роллбэк / Факторы: Задержка - У кого: Только у меня, Один регион или провайдер / Когда: Всегда, Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента, Команда инфраструктуры · Сетевая инфраструктура, Внешние стороны · Внешние стороны - Команда разработки, задачи: Сервер: выбирать регион по пингу до каждого региона, измеренному клиентом, вместо GeoIP, ограничить пинг в правиле расширения на дальние регионы, для группы смотреть средний пинг вместе с самым высоким пингом участника, писать в лог выбранный регион и пинг на тот момент. Клиент: измерять пинг до каждого региона по UDP и отправлять его вместе с запросом на матчмейкинг, показывать на экране регион подключения и пинг, дать возможность выбрать регион вручную. - Команда инфраструктуры, задачи: Если регион выбирается через DNS, проверить, поддерживает ли авторитативный DNS EDNS Client Subnet (если резолвер игрока его не передаёт, выбор идёт по местоположению резолвера), регулярно обновлять базу GeoIP, добавлять к логам подключений серверов в каждом регионе страну и ASN по GeoIP и искать страны и провайдеров, которые попадают в дальний регион. - Внешние стороны, задачи: Посоветовать игроку выключить VPN или игровой ускоритель и переподключиться, игрокам с корпоративным или зарубежным DNS посоветовать перейти на DNS провайдера, запросить у поставщика GeoIP исправление неверного местоположения. - Цифры для ориентира: Если игрока из Сеула отправить вместо Токио в регион на западе США, пинг вырастает примерно с 30 ms до примерно 130 ms. GeoIP верно определяет страну примерно в 99,8% случаев, но с точностью до города даже в США в пределах 50 km попадает только около 66%, а с VPN вместо игрока определяется местоположение VPN-сервера. - На графике: Высоко только у некоторых (RTT (пинг) по игрокам, распределение по выбранным регионам) - Где смотреть: Добавить к IP клиентов из журналов подключений серверов каждого региона (логи доступа балансировщика, VPC Flow Logs) страну и ASN по GeoIP и посчитать, к какому региону подключаются игроки из каждой страны и от каждого провайдера. Для одного игрока сравнить регион, к которому он реально подключён, с пингом до ближнего региона (измеряет игрок, или mtr до IP игрока с сервера в том регионе) - Подтверждает: Игроки и страны с высоким RTT подключены к дальнему региону вместо ближнего, а пинг до ближнего региона низкий - Опровергает: Если игрок правильно попал в ближний регион, а пинг всё равно высокий, причина в неоптимальной маршрутизации или в подключении и Wi-Fi игрока - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: При выборе региона через DNS (DNS с маршрутизацией по географии или задержке) местоположение угадывается по адресу DNS-резолвера, которым пользуется игрок, вместо адреса самого игрока. Если резолвер не поддерживает EDNS Client Subnet (передачу части адреса пользователя), игроки с корпоративным или далёким DNS получают регион по месту резолвера. Система матчмейкинга тоже может оценивать группу по среднему пингу участников или после долгого ожидания расширять порог пинга и отправлять в дальний регион. В AWS GameLift Servers пинг группы по умолчанию тоже считается как среднее, а в качестве примера приводится настройка, которая расширяет лимит пинга с 50 ms до 100 ms и 200 ms. У игрока с включённым 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%, на уровне города в США (в пределах 50 km) около 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 · Игра размещается в локации с наименьшей средней задержкой для всех игроков, но игроки с экстремальной задержкой тоже попадают в игру. Пример политики, которая расширяет лимит пинга с 50 ms до 100 ms и 200 ms - [Amazon GameLift Servers UDP ping beacons](https://docs.aws.amazon.com/gameliftservers/latest/developerguide/reference-udp-ping-beacons.html) · AWS · Игровой клиент измеряет задержку через UDP-эндпоинты в каждой локации хостинга и использует её для размещения и матчмейкинга. Это ближе к реальному игровому трафику, чем ICMP-пинг - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Медиана измеренного пути туда и обратно от Сеула (Korea Central): Токио (Japan East) 29 ms, запад США 124–136 ms - [Flow log records](https://docs.aws.amazon.com/vpc/latest/userguide/flow-log-records.html) · AWS · srcaddr в записи VPC Flow Logs: для входящего трафика это IP-адрес отправителя #### in-cert · Истёкший или неверно настроенный TLS-сертификат · TLS certificate expiry / misconfiguration Если у сервера авторизации, API или патчей истекает сертификат или пропадает промежуточный сертификат, с этого момента у новых подключений клиентов не устанавливается TLS-соединение. - Почему → Следствие → На экране: Срок действия сертификата истёк, сервер отдаёт цепочку без промежуточного сертификата, или на устройстве игрока неверные дата и время → Клиент не проходит проверку сертификата и разрывает TLS-соединение → Ошибка входа или бесконечная загрузка на этапе входа или патча, не работают только функции через HTTPS, например магазин. У тех, кто уже был в игре, обычно всё в порядке - Симптомы: Ошибка входа / бесконечная загрузка, Съеденные действия / роллбэк / Факторы: Остановка - У кого: Весь сервер, Только одна функция, Только у меня / Когда: Сразу после входа или техработ, При определённом действии - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка клиента - Команда разработки, задачи: Записывать ошибку сертификата отдельным кодом, отличным от других сбоев подключения, и показывать сообщение, при ошибке даты советовать включить автоматическую установку даты и времени на устройстве, при закреплении сертификата (pinning) добавить резервный ключ и согласовать график смены сертификатов с командой инфраструктуры. - Команда инфраструктуры, задачи: Сеть: если TLS терминируется на балансировщике или CDN, следить за автообновлением управляемых сертификатов и настроить алерт на число оставшихся дней (в ACM это DaysToExpiry), сохранять DNS-записи для проверки. Серверы и ОС: если 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) и число успешных входов - Подтверждает: Срок действия истёк или в отдаваемом списке нет промежуточного сертификата, а рост ошибок начался в момент истечения или замены сертификата - Опровергает: Если список сертификатов и сроки в порядке, а ошибка только у части игроков, проверить дату и время на их устройствах или список корневых сертификатов в старой ОС - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Конфигурация без промежуточного сертификата может выглядеть рабочей, если открыть адрес в браузере на ПК. Браузер помнит промежуточные сертификаты, полученные с других сайтов, и подставляет недостающие, а клиент без такой памяти, например Android-приложение, получает ошибку. Сроки действия тоже сокращаются. Let’s Encrypt планирует уменьшить стандартный срок до 64 дней в 2027 году и до 45 дней в 2028 году, поэтому при жёстко заданном обновлении раз в 60 дней у 64-дневного сертификата запас всего четыре дня, а 45-дневный успеет истечь. AWS Certificate Manager к тому же не обновляет автоматически импортированные (import) сертификаты, а если удалить 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 · Стандартный срок действия сокращается до 64 дней с февраля 2027 года и до 45 дней с февраля 2028 года. Обновления с фиксированным интервалом 60 дней станет недостаточно, рекомендуется обновлять примерно на 2/3 срока действия - [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 · Импортированные (import) и уже истёкшие сертификаты автоматически не обновляются - [Supported CloudWatch metrics](https://docs.aws.amazon.com/acm/latest/userguide/cloudwatch-metrics.html) · AWS · DaysToExpiry: число дней до истечения сертификата, публикуется два раза в сутки, пока сертификат не истёк - [Security with network protocols](https://developer.android.com/privacy-and-security/security-ssl) · Android (Google) · Если сервер отдаёт цепочку без промежуточного сертификата, Android-приложение получает SSLHandshakeException, а браузер на ПК может подставить сохранённый промежуточный сертификат и обойтись без ошибки. Цепочку, которую отдаёт сервер, проверяют через 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 или мобильной сети, чтобы потерять место → Ошибка входа / бесконечная загрузка, игра закрывается с ошибкой во время ожидания, ждать приходится снова с конца очереди - Симптомы: Ошибка входа / бесконечная загрузка, Дисконнект / Факторы: Остановка - У кого: Весь сервер, Только у меня / Когда: Сразу после входа или техработ, Вечерний пик - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента, Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Сервер: подогнать лимит очереди под реальную производительность сервера авторизации, на какое-то время сохранять место игрока, у которого оборвалось соединение в очереди (окно переподключения), показывать номер в очереди и ожидаемое время, писать в метрики длину очереди, число отказов и число обрывов в очереди. Клиент: при обрыве в очереди автоматически переподключаться на то же место, не закрывая игру, разносить повторные попытки во времени экспоненциальной задержкой (backoff) со случайным разбросом (джиттером). - Команда инфраструктуры, задачи: Серверы и ОС: до релиза измерить нагрузочным тестом предел серверов авторизации и лобби, к релизу подготовить резервные машины, которые можно быстро подключить, смотреть метрики очереди на одном графике с числом попыток подключения. - Цифры для ориентира: При выходе дополнения FINAL FANTASY XIV в 2021 году, когда в очереди одного логического дата-центра было больше 17 000 человек, новые места не выдавались (Error 2002). Если соединение обрывалось во время ожидания, лобби-сервер ждал от нескольких десятков секунд до 1 минуты, и игрок, успевший переподключиться, продолжал с того же места в очереди. - На графике: Упор в лимит (плато) (длина очереди на вход, число отказов из-за лимита, число обрывов во время ожидания) - Где смотреть: Вывести на один график с числом попыток подключения длину очереди, среднее время ожидания, число отказов из-за лимита и число обрывов во время ожидания по данным серверов авторизации и лобби - Подтверждает: Сразу после релиза или техработ длина очереди упирается в лимит и выходит на плато, в это время растёт число отказов, а обрывы во время ожидания приходятся в основном на игроков с Wi-Fi и мобильной сетью - Опровергает: Если очередь короткая, а вход медленный, причина в БД (db-login-storm) или в очереди подключений ОС (so-backlog) - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Замедление БД из-за наплыва входов разобрано в причине «Наплыв входов и запросы N+1», переполнение очереди подключений в ОС в причине «Переполнение очереди подключений (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 · Отсечение нагрузки (load shedding): лишние запросы отклоняются рано, чтобы сервер продолжал обрабатывать те, с которыми справляется ### Архитектура синхронизации (причин: 16) #### sy-request-response · Отклик только после ответа сервера (модель запрос-ответ) · Request-response (no client-side feedback) После нажатия кнопки нет ни анимации, ни звука, пока не придёт ответ сервера. Скорость отклика становится равна пингу. - Почему → Следствие → На экране: Умения, перемещение и подбор предметов проигрываются только после подтверждения сервера → С момента нажатия никакой реакции в течение пути туда и обратно плюс ожидания тика → При пинге 150 ms каждое действие запаздывает примерно на 0,2 с - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Только у меня / Когда: Всегда, При определённом действии - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: анимацию, звук и эффекты запускать сразу при нажатии (опережающий фидбек), результат (урон, награду) показывать только после подтверждения сервера, перемещение и базовую атаку предсказывать и применять сразу, а получив от сервера коррекцию позиции, повторно применять от этой позиции ещё не подтверждённый ввод. Сервер: самому рассчитывать перемещение по полученному вводу и отправлять коррекцию, только если расхождение с позицией, предсказанной клиентом, превышает порог. - Цифры для ориентира: Время отклика ≈ пинг + половина интервала тика + один кадр. На 20-тиковом сервере при пинге 150 ms около 190 ms. - На графике: Высоко с самого начала (время от ввода до начала анимации, RTT (пинг)) - Где смотреть: Писать в лог клиента в development-сборке время нажатия кнопки, начала первой анимации и звука и прихода ответа сервера и смотреть рядом с внутриигровым RTT. Менять пинг, добавляя задержку через эмуляцию сети в движке (Unreal NetEmulation.PktLag) или через tc netem в Linux на тестовом сервере, и замерять - Подтверждает: Анимация всегда начинается в момент прихода ответа сервера, время от ввода до анимации равно RTT плюс ожидание тика и растёт ровно на добавленную задержку - Опровергает: Если анимация начинается сразу при нажатии, а запаздывает только результат вроде цифр урона, архитектура нормальная. Если даже при низком пинге задержка больше интервала тика, это двойное ожидание тика или проблема с кадрами на клиенте - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Для игр, где быстрая реакция не нужна (пошаговые, карточные, idle-игры), эта модель самая простая и надёжная. Проблема возникает, когда в игре с управлением в реальном времени так же сделаны перемещение и даже базовая атака. - Источники: - [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 · Клиент, который ждёт только результата от сервера, при задержке 500 ms показывает каждое действие лишь через 500 ms. Решается клиентским предсказанием и серверной коррекцией - [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 Если для одного действия нужно несколько обменов с сервером по очереди, пинг умножается на их число. - Почему → Следствие → На экране: Открытие магазина → запрос списка → проверка цены → покупка → обновление инвентаря, каждое отдельным запросом → Следующий запрос уходит только после ответа на предыдущий → При пинге 150 ms одна покупка занимает почти 1 с. Загрузки подозрительно долгие - Симптомы: Задержка ввода, Ошибка входа / бесконечная загрузка / Факторы: Задержка - У кого: Только одна функция, Только у меня / Когда: При определённом действии, Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: изменить протокол так, чтобы несколько шагов шли одним запросом и ответом (например, класть обновлённый инвентарь в ответ на покупку). Клиент: заранее загружать нужные данные, делать UI, который не ждёт результата. - Цифры для ориентира: Время ≈ число обменов × (пинг + обработка на сервере + ожидание тика). При 5 обменах и пинге 150 ms около 0,85–1 с. - На графике: Высоко с самого начала (время выполнения по функциям, число обменов на одно действие) - Где смотреть: В захвате пакетов на стороне сервера (Wireshark) посчитать, сколько раз запросы и ответы сменяют друг друга за одно действие тестового аккаунта (покупка в магазине, вход) и с какими интервалами. Если есть лог запросов на сервере, сгруппировать по ID сессии и смотреть число запросов и время прихода и ответа каждого - Подтверждает: Одно действие состоит из нескольких последовательных запросов, каждый ждёт предыдущего ответа, время выполнения примерно равно числу обменов × RTT, и чем выше пинг в регионе игрока, тем пропорционально медленнее та же функция - Опровергает: Если обменов один-два, а долго идёт один ответ, причина в обработке на сервере или в БД. Если все игроки тормозят одинаково независимо от пинга, смотреть нагрузку на сервер - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [Chatty I/O antipattern](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/) · Microsoft Azure · Множество мелких запросов ввода-вывода накапливает задержку и сильно ухудшает отзывчивость. Рекомендуется объединять их в более крупные и редкие запросы #### sy-no-queue · Нет буферизации ввода умений · No input/spell queue Если следующее умение можно нажать только после подтверждения сервера, что предыдущее закончилось, в каждую связку вклинивается путь туда и обратно. - Почему → Следствие → На экране: Ввод следующего умения принимается только «после подтверждения предыдущего» → Между умениями появляется пустой промежуток длиной в пинг → Между приёмами связки появляются паузы, и чем выше пинг, тем ниже DPS - Симптомы: Задержка ввода, Съеденные действия / роллбэк / Факторы: Задержка - У кого: Только у меня, Только одна функция / Когда: При определённом действии - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: ввести окно буфера ввода, чтобы нажатие в течение некоторого времени до конца кулдауна (например, 0,3–0,4 с) принималось и сразу отправлялось на сервер. Сервер: принимать ввод, пришедший чуть раньше, и выполнять его в момент окончания кулдауна. - Цифры для ориентира: В связке с кулдауном 1 с при пинге 150 ms между умениями пустует 0,15 с и больше, и за то же время применяется больше чем на 13% меньше умений. - На графике: Высоко с самого начала (пустой промежуток между умениями, RTT (пинг)) - Где смотреть: Писать в лог сервера для каждого персонажа время окончания кулдауна, время прихода запроса на следующее умение и время его выполнения, сравнивать пустой промежуток между ними с RTT игрока - Подтверждает: От окончания кулдауна до выполнения следующего умения всегда проходит примерно RTT, и чем выше пинг игрока, тем длиннее промежуток и тем меньше умений применено за то же время - Опровергает: Если промежуток постоянный и не зависит от пинга, это заложено в дизайне (глобальный кулдаун или длина анимации). Если промежуток лишь иногда резко растёт, смотреть джиттер и потери или превышение бюджета тика - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Например, в World of Warcraft есть окно буфера ввода, и игрок может настроить его длину. Если окно длиннее пути туда и обратно, пинг между приёмами связки почти не заметен. - Источники: - [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 2 с ростом задержки урон персонажа падает и бои затягиваются (при росте с 0 до 500 ms бой длиной около 2 минут удлиняется на 5 с) #### sy-short-window · Короткое окно реакции, которое съедает пинг · Timing window too short for latency + reaction Если на реакцию (уклонение, парирование, блок) отведено мало времени, пинг съедает это время, и появляются атаки, от которых невозможно уйти. - Почему → Следствие → На экране: Короткие окна реакции, например предупреждение об атаке босса за 0,5 с или окно парирования 0,2 с → Предупреждение игрок видит поздно (задержка на пути к нему + интерполяция), и его ввод тоже приходит поздно (задержка на пути к серверу + ожидание тика) → Точно увернулся, а удар прошёл, парирование «съело» - Симптомы: Съеденные действия / роллбэк, Задержка ввода / Факторы: Задержка - У кого: Только у меня, Только одна функция / Когда: При определённом действии - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента, Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Сервер: планировать предупреждение об атаке по серверному времени и отправлять его заранее, расширять окно реакции на величину пинга (компенсация задержки). Клиент: проигрывать полученное предупреждение в запланированный момент по серверному времени. - Команда инфраструктуры, задачи: Размещать серверы ближе к регионам, где много игроков (региональные серверы), чтобы уменьшить сам пинг. - Цифры для ориентира: При пинге 150 ms и интерполяции 100 ms предупреждение появляется на экране игрока примерно через 0,18 с, а ввод игрока доходит до сервера примерно за 0,1 с. Если добавить 0,25 с на реакцию человека, увернуться от атаки с предупреждением за 0,5 с почти невозможно. - На графике: Высоко только у некоторых (доля неудачных уклонений и парирований (по диапазонам пинга)) - Где смотреть: Писать в лог сервера время начала и конца окна реакции, время прихода ввода игрока на сервер и RTT этого игрока, смотреть долю неудач в разбивке по диапазонам пинга (например, с шагом 50 ms) - Подтверждает: Чем выше диапазон пинга, тем заметно выше доля неудач, а неудачный ввод приходит вскоре после закрытия окна (в пределах суммы RTT и времени интерполяции) - Опровергает: Если доля неудач примерно одинакова во всех диапазонах пинга, дело в сложности паттерна. Если ввод пришёл внутри окна, а засчитан как неудача, смотреть код проверки или серверную валидацию - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · Среднее время простой реакции около 231 ms (213 ms с поправкой на задержку оборудования), в недавних крупных исследованиях 233–400 ms - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · Чем точнее действие и чем короче срок на него, тем оно чувствительнее к задержке (предел около 100 ms для вида от первого лица, около 500 ms для вида от третьего лица, около 1 000 ms для всевидящей камеры сверху) - [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 с назад (при пинге 150 ms и интерполяции 100 ms) → Сервер проверяет по текущей позиции, и там, куда целился игрок, цели уже нет → Точно попал, а промах. По движущейся цели приходится стрелять с упреждением - Симптомы: Съеденные действия / роллбэк / Факторы: Задержка - У кого: Только у меня / Когда: При определённом действии - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: проверять попадание, отмотав время назад к моменту, который видел атакующий (компенсация задержки), или перейти на выбор цели (таб-таргет). Клиент: при атаке отправлять момент, который видел игрок (серверное время, на котором идёт интерполяция). - На графике: Высоко только у некоторых (точность по движущимся целям (по диапазонам пинга)) - Где смотреть: Писать в лог проверок на сервере вместе время атаки, позицию цели на экране атакующего (значение от клиента), позицию цели на сервере, по которой шла проверка, и RTT атакующего. Если в development-сборке рисовать поверх экрана клиента позицию, которую сервер использовал для проверки, расхождение видно сразу - Подтверждает: В промахах разница двух позиций примерно равна скорости цели × (RTT атакующего + время интерполяции), и с ростом пинга падает точность только по движущимся целям - Опровергает: Если промахи бывают и по неподвижным целям, проблема в хитбоксах или проверке столкновений. Если отмотка времени есть, а расхождение остаётся, проверить, правильно ли клиент сообщает серверу время интерполяции - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [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 Если отматывать время слишком далеко в пользу атакующего, в того, кто уже спрятался, всё равно попадают. - Почему → Следствие → На экране: Ради атакующего с высоким пингом сервер проверяет попадание с большой отмоткой назад → На экране того, в кого попали, он уже был в укрытии → «В меня попали, когда я уже был за стеной», преимущество у игроков с высоким пингом - Симптомы: Съеденные действия / роллбэк / Факторы: Задержка - У кого: Только у меня, Один регион или провайдер / Когда: При определённом действии - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Ограничить глубину отмотки (например, 200–250 ms), атакующему с более высоким пингом отматывать только до лимита, а остальное он компенсирует упреждением сам. - На графике: Высоко только у некоторых (глубина отмотки для каждого попадания (по пингу атакующего)) - Где смотреть: Писать в лог проверок на сервере для каждого попадания глубину отмотки, RTT атакующего и серверное время, когда цель зашла в укрытие. В development-сборке рисовать на экране отмотанные хитбоксы (в движке Source это sv_showlagcompensation) - Подтверждает: Попадания из жалоб «попали, когда я уже был за стеной» приходятся на атакующих с большой глубиной отмотки, а глубина отмотки растёт вслед за пингом атакующего без всякого лимита - Опровергает: Если попадания по игроку за стеной бывают и при малой глубине отмотки, проблема в хитбоксах или проверке столкновений. Если высокий пинг у того, в кого попали, его перемещение просто поздно дошло до сервера - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Проверка попадания с отмоткой работает по принципу «приоритет стреляющего». Предложено и исключение «приоритет цели»: если цель на своём экране уже зашла в безопасное место, отмотка не выполняется. - Источники: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Без лимита отмотки игрок с задержкой 500 ms может попасть по цели через 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 Если каждый клиент сам определяет свои результаты, на экране игрока всё плавно, но результаты расходятся с экранами других, и игра уязвима для читов. - Почему → Следствие → На экране: Позицию и попадания определяет клиент, а сервер только пересылает → Два игрока утверждают, что каждый попал первым, а сервер не может это проверить → Противник телепортируется или проходит сквозь стены, «я попал, а урона нет» - Симптомы: Телепортация, Съеденные действия / роллбэк / Факторы: Задержка - У кого: Весь сервер / Когда: Всегда - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: важные результаты (попадания и т. п.) проверять самому, перемещение проверять по скорости и расстоянию. Клиент: получив от сервера отказ или коррекцию, возвращать состояние к значению сервера. - На графике: Высоко с самого начала (число отчётов с невозможной скоростью перемещения и противоречащих друг другу попаданий) - Где смотреть: Записывать на сервере позиции и попадания в том виде, в каком их прислал клиент, по последовательным отчётам о позиции считать скорость перемещения и подсчитывать отчёты с превышением максимальной скорости и случаи, когда два игрока сообщают, что каждый попал первым - Подтверждает: Сервер пересылает отчёты другим клиентам без проверки, а невозможные скорости и противоречащие попадания появляются постоянно, независимо от патча и региона - Опровергает: Если сервер сам рассчитывает или проверяет результаты, причина не эта. Тогда при телепортации смотреть потери пакетов или буфер интерполяции - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [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 · Lockstep waits for the slowest peer В схеме, где все вместе рассчитывают один и тот же ход, при опоздании ввода одного игрока ждут все. - Почему → Следствие → На экране: Каждый ход можно рассчитать, только собрав ввод всех игроков → Ввод одного игрока приходит поздно из-за джиттера или потерь → У всех одновременно замирание, в тяжёлых случаях окно «Ожидание игрока» - Симптомы: Фриз, Микрофризы, Задержка ввода / Факторы: Джиттер, Потери, Остановка - У кого: Одна локация или канал / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: автоматически подстраивать задержку применения ввода под пинг, ненадолго исключать только опаздывающего игрока, чтобы остальные продолжали без ожидания. Клиент: соблюдать заданную задержку применения ввода; в P2P без промежуточного сервера подстройку этой задержки и обработку опаздывающих тоже берёт на себя клиент-хост. - Цифры для ориентира: Если задержку применения ввода (input delay) сделать меньше, чем «время доставки ввода сопернику + джиттер», фризы станут частыми. Время доставки при прямом обмене равно половине пинга, а через промежуточный сервер примерно половине суммы пингов двух игроков. - На графике: Случайные всплески (время ожидания хода, задержка прихода ввода по игрокам) - Где смотреть: Записывать для каждого хода время прихода ввода от каждого игрока и время, которое ход простоял в ожидании, и смотреть, чьего ввода ждал остановившийся ход. Если есть промежуточный сервер, это видно и по интервалам прихода пакетов ввода от каждого игрока в захвате пакетов на сервере - Подтверждает: В каждом остановившемся ходе ввод одного и того же игрока пришёл позже, чем позволяет задержка применения ввода, и в это время у него подскакивают джиттер и потери - Опровергает: Если весь ввод пришёл вовремя, а игра всё равно стоит, проблема во времени расчёта на самом медленном ПК или в обработке на сервере. Если фризов нет, а расходятся только результаты на двух экранах, это рассинхронизация результатов расчёта (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 · Команды планируются на исполнение через два хода, а длина хода подстраивается под самый медленный компьютер и пинг (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 Игра предсказывает ввод соперника и показывает результат заранее, а при ошибке отматывает назад и пересчитывает. Чем больше пинг, тем глубже отмотка. - Почему → Следствие → На экране: Соперник сменил ввод (не так, как предсказано) → Реальный ввод приходит с опозданием на половину пинга, и на столько же приходится отматывать назад и пересчитывать → Движение соперника проскакивает несколько кадров или внезапно меняется - Симптомы: Телепортация / Факторы: Задержка, Джиттер - У кого: Только у меня / Когда: При определённом действии, Изредка, случайно - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Добавить задержку применения ввода (input delay) в 1–3 кадра, чтобы уменьшить глубину отмотки, ограничить глубину отмотки. - Цифры для ориентира: При пинге 100 ms (50 ms в одну сторону) и 60 fps отматывается около 3 кадров. Если задержать применение ввода на 2 кадра, отмотка сократится до 1 кадра. - На графике: Случайные всплески (число отмотанных кадров, RTT (пинг)) - Где смотреть: Записывать на клиенте при каждой отмотке число отмотанных кадров, RTT в этот момент, настройку задержки применения ввода и время, ушедшее на отмотку и пересчёт - Подтверждает: В моменты, когда движение соперника дёрнулось, число отмотанных кадров большое, средняя глубина отмотки примерно равна (задержка в одну сторону − задержка применения ввода) ÷ время кадра и растёт с пингом - Опровергает: Если отмотка небольшая, а микрофризы есть, это проблема производительности: пересчёт не укладывается в один кадр. Если и после отмотки результаты на двух экранах расходятся, это рассинхронизация (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 · Роллбэк убирает локальную задержку применения ввода в lockstep, за 16 ms отматывается и пересчитывается до 8 кадров #### sy-no-timestamp · Воспроизведение сразу по приходу без меток времени · Events played on arrival (no timestamps) Если сервер не прикрепляет к событиям время, когда они произошли, а клиент проигрывает их сразу при получении, сетевой джиттер напрямую сбивает тайминг анимаций. - Почему → Следствие → На экране: События «начало атаки», «запуск эффекта» выполняются сразу по приходу → У каждого пакета своё время доставки, и интервалы скачут → Серия атак то ускоряется, то замедляется, тайминг паттернов босса каждый раз разный - Симптомы: Микрофризы, Перемотка / Факторы: Джиттер - У кого: Только у меня / Когда: Всегда - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: проигрывать по времени, прикреплённому к событию (планирование событий, буфер интерполяции). Сервер: прикреплять к событиям время, когда они произошли (серверное время). - На графике: Случайные всплески (интервалы воспроизведения событий, интервалы прихода пакетов) - Где смотреть: Сопоставить по номеру события время из лога сервера с временем прихода и воспроизведения из лога клиента и сравнить интервалы. В development-сборке воспроизвести, добавив джиттер (значение джиттера в 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 · К каждому обновлению прикрепляется серверное время, и объект рисуется в позиции на момент «текущее время минус время интерполяции (100 ms)» - [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 Если запросы копятся до следующего тика и только тогда обрабатываются, а результат уходит ещё через тик, интервал тика добавляется дважды. - Почему → Следствие → На экране: Полученный запрос обрабатывается в следующем тике → Результат тоже копится и уходит в следующем тике отправки → Сетевой пинг низкий, а отклик стабильно запаздывает примерно на 1,5 интервала тика. На 10-тиковом сервере в среднем 0,15 с, в худшем случае 0,2 с - Симптомы: Задержка ввода / Факторы: Задержка - У кого: Весь сервер / Когда: Всегда - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Отправлять ответ сразу в тике обработки, поднять тикрейт, важные ответы отправлять немедленно. - Цифры для ориентира: На 10-тиковом сервере один тик длится 100 ms, поэтому одно только ожидание тиков добавляет в среднем 150 ms, в худшем случае 200 ms. При одном ожидании в среднем 50 ms. - На графике: Высоко с самого начала (время от прихода запроса до отправки ответа) - Где смотреть: В захвате пакетов на стороне сервера измерить интервал между приходом пакета запроса и уходом пакета ответа, когда тестовый аккаунт несколько раз выполняет одно и то же действие (например, использует предмет). Если есть лог сервера, смотреть время прихода запроса, номер тика обработки и время отправки ответа - Подтверждает: Время внутри сервера в среднем около 1,5 интервала тика, максимум около 2 интервалов, и от RTT оно не зависит - Опровергает: Если время внутри сервера около половины интервала тика, ожидание тика только одно. Если оно скачет и бывает дольше интервала тика, смотреть превышение бюджета тика - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Пришедший ввод ждёт границы тика до одного тика, и ещё один кадр уходит на применение и отправку. Чем выше тикрейт, тем меньше задержка - [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 Если сервер слишком строго проверяет скорость перемещения, кулдауны и дальность, он отклоняет даже нормальный ввод, пришедший пачкой из-за джиттера. - Почему → Следствие → На экране: Жёсткие критерии вроде «расстояние, доступное за один тик» или «допуск по кулдауну 0 ms» → Если из-за джиттера две команды приходят в одном тике, это засчитывается как нарушение правил → Откидывание назад, умение отклоняется, хотя кулдаун прошёл - Симптомы: Откидывание назад, Съеденные действия / роллбэк / Факторы: Джиттер - У кого: Только у меня / Когда: Изредка, случайно, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Проверять по накопленному лимиту (token bucket), давать запас на пинг и джиттер. - На графике: Случайные всплески (число отказов серверной проверки и коррекций позиции) - Где смотреть: Писать в лог сервера для каждого отказа проверки и коррекции позиции причину, число команд этого игрока, пришедших в том тике, и интервал прихода после предыдущей команды - Подтверждает: Отказы и коррекции приходятся на моменты, когда в одном тике пришли 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 · Token bucket: решение принимается по средней скорости (CIR) и допустимому размеру всплеска (CBS) #### sy-host · Архитектура с хостом-игроком · Listen server / host advantage Если сервером служит ПК одного из игроков, его подключение и производительность ПК определяют ощущения всех. - Почему → Следствие → На экране: Сервером служит ПК хоста (P2P, listen-сервер) → Медленное подключение или ПК хоста сказывается на всех, а у самого хоста пинг 0 → Преимущество только у хоста, когда хост выходит, у всех фриз или дисконнект - Симптомы: Микрофризы, Фриз, Дисконнект / Факторы: Задержка, Остановка - У кого: Одна локация или канал / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента, Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Сервер: перейти на выделенный сервер, который проверяет действия, а до этого при матчмейкинге выбирать хостом игрока с хорошим подключением и ПК. Клиент: поддерживать миграцию хоста, при матчмейкинге измерять и отправлять пинг до других участников, скорость отдачи и производительность ПК. - Команда инфраструктуры, задачи: Выделить серверное оборудование или инстансы под выделенные серверы, размещать их ближе к регионам, где много игроков. - На графике: Высоко только у некоторых (число жалоб на лаги и дисконнектов по хостам) - Где смотреть: Писать в лог матча скорость отдачи хоста, RTT каждого участника до хоста, время кадра на ПК хоста и момент выхода хоста, группировать жалобы на лаги и дисконнекты по хостам. Игроки тоже могут проверить: сыграть тем же составом, сменив только хоста - Подтверждает: Лаги и дисконнекты сосредоточены в комнатах одного хоста, когда у него низкая скорость отдачи или долгий кадр, хуже становится всем участникам сразу, а без миграции хоста в момент его выхода у всех дисконнект - Опровергает: Если независимо от хоста плохо только участникам из одного региона, проблема в подключении или маршруте. При выделенных серверах причина не эта - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [Networking Overview for Unreal Engine](https://dev.epicgames.com/documentation/en-us/unreal-engine/networking-overview-for-unreal-engine) · Epic Games · Хост listen-сервера получает преимущество перед другими клиентами и несёт большую нагрузку, потому что одновременно работает сервером и рендерит - [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 Если сервер потом не засчитывает удар или умение, которые экран игрока уже показал, результат, который игрок точно видел, отменяется. - Почему → Следствие → На экране: Эффект удара и анимация умения проигрываются до подтверждения сервера (опережающий фидбек) → Сервер заново проверяет дальность, позицию цели, кулдаун и ресурсы и отклоняет действие → Кровь брызнула, а урона нет; анимация умения проиграна, а эффекта нет; запустился только кулдаун - Симптомы: Съеденные действия / роллбэк, Откидывание назад / Факторы: Задержка - У кого: Только у меня, Только одна функция / Когда: При определённом действии - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: по результату сервера показывать только то, что требует подтверждения (цифры урона, смерть, награды), частые причины отказа проверять заранее, при отказе возвращать кулдаун и ресурсы и показывать причину. Сервер: давать запас на пинг при проверке дальности и позиции цели, отправлять причину в ответе с отказом, собирать долю отказов по умениям в метрики. - Цифры для ориентира: Отказ приходит с опозданием на пинг плюс ожидание тика после нажатия. При пинге 150 ms игрок около 0,2 с считает, что попал. - На графике: Высоко только у некоторых (доля отказов сервера по умениям (по диапазонам пинга)) - Где смотреть: Собирать на сервере долю отказов по умениям и причины отказов (дальность, позиция цели, кулдаун, ресурсы) в разбивке по диапазонам RTT игроков. На клиенте записывать, сколько раз действия с опережающим фидбеком были отклонены - Подтверждает: Отказы сосредоточены на отдельных умениях и причинах «дальность» и «позиция цели», и с ростом пинга доля отказов растёт - Опровергает: Если причина отказа кулдаун или ресурсы и от пинга это не зависит, проверить, не расходятся ли значения данных (кулдаун, стоимость) у клиента и сервера. Если отказов нет, а анимация начинается только после ответа сервера, это причина «Отклик только после ответа сервера (модель запрос-ответ)» - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Опережающий фидбек лучше всего скрывает пинг. Но чем сильнее расходятся данные, по которым решают клиент и сервер (позиция противника, оставшиеся ресурсы), тем чаще отказы. Если собирать долю отказов по умениям в метрики, места, где проверки расходятся, легко найти. - Источники: - [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 Если стороны обмениваются только командой «иди сюда», а путь каждая сторона рассчитывает сама, то даже при небольшом расхождении персонаж или монстр идёт другим путём, а потом его утягивает на правильное место. - Почему → Следствие → На экране: При перемещении кликом и преследовании монстром отправляется только точка назначения, а путь клиент рассчитывает сам → Из-за различий в данных ландшафта, столкновений с другими персонажами или порядка расчёта объект идёт не тем путём, что на сервере → Монстр проходит сквозь стену и вдруг перескакивает на другое место, персонаж после клика как бы скользит и меняет направление - Симптомы: Телепортация, Откидывание назад / Факторы: Задержка - У кого: Одна локация или канал, Только у меня / Когда: В движении и при смене локации - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: отправлять вместе с целью промежуточные точки пути (вейпоинты), периодически синхронизировать позицию. Клиент: плавно сводить расхождение, использовать те же данные ландшафта, что и сервер. - На графике: Случайные всплески (число и расстояние коррекций позиции по объектам) - Где смотреть: Записывать для каждого объекта разницу между позицией от сервера и позицией, рассчитанной клиентом, и отмечать на карте точки, где происходили коррекции. Если периодически сравнивать контрольные суммы результатов расчёта пути или позиций с обеих сторон, можно найти момент, когда началось расхождение - Подтверждает: Коррекции сосредоточены на определённом рельефе (пороги, узкие проходы, склоны) или в людных местах и повторяются в тех же точках даже у игроков с нормальными сетевыми метриками - Опровергает: Если коррекции бывают где угодно, но только в моменты всплесков потерь и джиттера, проблема в подключении. Если один монстр одновременно дёргается на экранах нескольких игроков, проверить, не находится ли управление монстром у тормозящего клиента - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Это одна из причин, почему игры с перемещением кликом и таб-таргетом малочувствительны к пингу. Однако одинаковый результат у обеих сторон не гарантирован, поэтому обязательно нужен механизм, который время от времени синхронизирует позицию. Результаты вычислений с плавающей точкой могут немного отличаться в зависимости от типа CPU, компилятора и его настроек оптимизации (в том числе между debug- и release-сборками). В схемах вроде lockstep и роллбэка, где стороны обмениваются только вводом и считают, что результаты расчёта у них совпадают, эти мелкие различия накапливаются, и состояние игры на двух экранах может разойтись (desync). - Источники: - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · Даже если на одной машине расчёт детерминирован, на другом компиляторе, ОС или 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 · При потерях пакетов или когда два персонажа пытаются встать в одну точку, симуляции сервера и клиента расходятся, и нужна коррекция - [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 и сборки (debug или release). Известен случай, когда CPU AMD и Intel выдавали немного разные значения трансцендентных функций - [/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 Если сервер отправляет обновления позиций (снапшоты) всего несколько раз в секунду, буфер интерполяции приходится делать соответственно длинным, и другие персонажи видны в более далёком прошлом. - Почему → Следствие → На экране: Ради экономии трафика обновления позиций отправляются всего 5–10 раз в секунду → Для плавной картинки буфер нужен в 2 раза длиннее интервала между пакетами (200–400 ms), а если сделать его коротким, потеря одного пакета уже даёт фриз → Смена направления у противника видна поздно и расходится с проверкой попадания. При коротком буфере микрофризы, при потерях телепортация - Симптомы: Микрофризы, Телепортация, Съеденные действия / роллбэк / Факторы: Задержка, Потери - У кого: Весь сервер / Когда: Всегда - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: близкие объекты и тех, кто в бою, обновлять часто, а дальние редко, отправлять только изменения (дельта-сжатие), чтобы уменьшить размер пакета и поднять частоту. Клиент: автоматически подстраивать длину буфера интерполяции под интервал между пакетами. - Цифры для ориентира: При 10 обновлениях в секунду интервал между пакетами 100 ms, буфер 200 ms. Если добавить 75 ms задержки в одну сторону при пинге 150 ms, противник виден примерно на 0,3 с в прошлом. - На графике: Высоко с самого начала (интервалы прихода пакетов по клиентам, длина буфера интерполяции) - Где смотреть: В захвате пакетов на стороне сервера отфильтровать только поток к одному игроку и смотреть в I/O Graphs Wireshark число пакетов в секунду и интервалы. Если есть игровые логи, смотреть вместе интервалы обновлений по объектам и запас буфера интерполяции на клиенте (сколько времени осталось до следующего снапшота) - Подтверждает: Обновления позиций всегда редкие, 5–10 в секунду (интервал 100–200 ms), буфер интерполяции больше 200 ms или его запас часто падает до 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 обновлениях в секунду интерполяция 200 ms переживает один пропуск. В Half-Life по умолчанию 20 обновлений в секунду и интерполяция 100 ms - [Snapshot Interpolation](https://gafferongames.com/post/snapshot_interpolation/) · Gaffer On Games · При 10 пакетах в секунду, чтобы пережить две потери подряд, нужна задержка 350 ms, при 30 пакетах в секунду она сокращается до 150 ms - [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 → Сервер применяет их разом в тике получения, и позиция персонажа меняется ступеньками → На экранах других игроков только этот персонаж замирает, а потом разом проскакивает вперёд. Остальное в порядке - Симптомы: Перемотка, Телепортация / Факторы: Джиттер, Потери - У кого: Странно выглядит один персонаж, Один регион или провайдер / Когда: Всегда, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Внешние стороны · Внешние стороны - Команда разработки, задачи: Применять команды равномерно через буфер ввода на игрока, применять их с исходными интервалами по порядковым номерам ввода. Одного увеличения буфера интерполяции на чужих экранах мало: на сервере сама история позиций уже ступенчатая. - Внешние стороны, задачи: Посоветовать тормозящему игроку проводное подключение и проверку Wi-Fi и роутера. - Цифры для ориентира: При джиттере 80 ms на 20-тиковом сервере (тик 50 ms) число команд за тик скачет от 0 до 3. - На графике: Высоко только у некоторых (число применённых команд за тик по игрокам, джиттер по игрокам) - Где смотреть: В захвате пакетов на стороне сервера отфильтровать пакеты от игрока, на которого жалуются, посчитать, сколько их приходит за каждый интервал тика (например, 50 ms), и сравнить с другими игроками. Если есть лог сервера, смотреть по игрокам число применённых команд перемещения за тик и порядковые номера ввода - Подтверждает: Только пакеты этого игрока приходят пачками, то 0, то 2–3 за тик, у него высокие джиттер и потери, а пакеты других игроков приходят равномерно. Когда этот игрок переходит на кабель, становится лучше - Опровергает: Если пачками движутся сразу несколько персонажей, причина в задержке тика сервера или в подключении того, кто смотрит. Если приход и применение равномерны, а дёргается только этот персонаж, проблема в интерполяции или отображении на стороне смотрящего - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: В архитектуре с авторитетным сервером это нормальное поведение. Лаги одного тормозящего игрока другие видят только как «странное движение этого игрока», а на управление других игроков и движение монстров они не влияют. Однако всё, что напрямую связано с этим игроком (обмен, механики группы, проверки попаданий в 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 · Если применять ввод сразу по приходу, даже при отправке с частотой 60 Hz интервалы неравномерны и результат скачет #### pt-input-buffer · Размер буфера ввода на игрока · Per-player server input buffer (jitter buffer) Если сервер немного накапливает ввод каждого игрока и берёт по одной команде за тик, на чужих экранах движение плавное, но момент, когда действие самого игрока подтверждается сервером, сдвигается на столько же. - Почему → Следствие → На экране: Сервер накапливает ввод тормозящего игрока в буфере и применяет по одной команде за тик → Маленький буфер часто пустеет, и персонаж встаёт на месте или сервер двигает его, угадывая по последнему вводу, а большой буфер задерживает подтверждение ввода самого игрока → С маленьким буфером другие видят замирания, с большим результат умений самого игрока появляется поздно (задержка ввода) - Симптомы: Микрофризы, Задержка ввода / Факторы: Джиттер - У кого: Странно выглядит один персонаж, Только у меня / Когда: Всегда - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: автоматически подстраивать размер буфера под состояние подключения каждого игрока, при отставании брать по две команды и догонять, клиентам игроков, у которых буфер часто пустеет, давать команду отправлять ввод раньше. Клиент: по команде сервера отправлять ввод немного раньше (подстройка времени клиента). - Цифры для ориентира: Зависит от игры, но обычно 1–3 тика. VALORANT на 128-тиковом сервере держит серверный буфер ещё короче, в среднем полкадра (около 4 ms). Часто используют адаптивный буфер, который увеличивается только у игроков с большим джиттером. - На графике: Высоко только у некоторых (длина буфера ввода и число опустошений по игрокам) - Где смотреть: Писать на сервере для каждого игрока число команд в буфере ввода на каждом тике, число случаев, когда буфер опустел и был заполнен догадкой по последнему вводу, и время от прихода ввода до его применения - Подтверждает: У игроков с маленьким буфером много опустошений, и в эти моменты на чужих экранах они ненадолго замирают, а у игроков с большим буфером время от ввода до применения выросло на длину буфера - Опровергает: Если буфер почти не пустеет, а на чужих экранах видны микрофризы, проблема в интерполяции на стороне смотрящего. Если буфер короткий, а задержка ввода большая, причина в самом 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 У игроков на подключениях с большим джиттером ввод приходит пачками и часто попадает под серверную проверку скорости и кулдаунов. - Почему → Следствие → На экране: Вечером растёт джиттер на подключениях определённого провайдера или региона → Нормальный ввод, пришедший пачкой, сервер считает превышением скорости или нарушением кулдауна → Только у абонентов этого провайдера откидывание назад и отказы умений, в тяжёлых случаях сервер кикает игрока (дисконнект) - Симптомы: Откидывание назад, Съеденные действия / роллбэк, Дисконнект / Факторы: Джиттер - У кого: Один регион или провайдер, Только у меня / Когда: Вечерний пик, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Сетевая инфраструктура - Команда разработки, задачи: Проверять накопленный лимит за несколько секунд, смягчать критерии с учётом состояния подключения (пинг, джиттер), ввести этап предупреждения перед киком, распределять пришедший пачкой ввод по тикам через буфер ввода на игрока, чтобы ложных срабатываний изначально было меньше. - Команда инфраструктуры, задачи: Смотреть распределение потерь и джиттера по провайдерам и времени суток и делиться им с командой разработки, проверять маршрут на участке этого провайдера (mtr в обе стороны), при необходимости менять маршрут или эскалировать провайдеру. - На графике: Высоко только в определённые часы (число отказов проверки и киков по провайдерам (ASN), джиттер по провайдерам) - Где смотреть: Добавить к логам отказов проверки, коррекций и киков на сервере провайдера (ASN) по IP подключения и время и посчитать по провайдерам и времени суток. Команда инфраструктуры в то же время запускает 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 · Token bucket: решение принимается по средней скорости и допустимому размеру всплеска - [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 · Один тормозящий участник группы и механики босса · One laggy member in a synchronized mechanic В рейдовых механиках, где все должны отреагировать в один и тот же момент, запоздалая реакция одного тормозящего игрока проваливает всю группу. - Почему → Следствие → На экране: Общие механики вроде «всем разом разбежаться» или «одному нажать кнопку» → Тормозящий игрок поздно видит предупреждение, и его ввод тоже приходит поздно → Вайп из-за одного игрока, остальные участники чувствуют, что «виноват тот, у кого лаги» - Симптомы: Съеденные действия / роллбэк, Задержка ввода / Факторы: Задержка - У кого: Одна локация или канал, Странно выглядит один персонаж / Когда: При наплыве игроков, При определённом действии - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: давать окнам механик запас на пинг, отправлять предупреждения заранее по серверному времени, проектировать так, чтобы ошибка одного не вела к вайпу. Клиент: проигрывать полученное предупреждение по серверному времени. - На графике: Высоко только у некоторых (RTT игроков, из-за которых провалилась механика) - Где смотреть: Писать в лог механик на сервере игрока, из-за которого провалилась механика, время прихода его ввода, окно механики и его RTT и потери - Подтверждает: Ввод, который привёл к вайпу, почти всегда от одного и того же игрока, его RTT заметно выше среднего по группе, а ввод приходит сразу после закрытия окна - Опровергает: Если провалы равномерно распределены по участникам, проблема в слишком коротком окне (причина «Короткое окно реакции, которое съедает пинг»). Если ввод тормозящего игрока пришёл внутри окна, а механика всё равно провалена, проблема в коде проверки на сервере - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [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 · Среднее время простой реакции около 231 ms #### pt-mob-control · Управление монстром у тормозящего клиента · Monster movement delegated to a player client В некоторых играх, чтобы снизить нагрузку на сервер, расчёт перемещения монстров поручают клиенту одного из ближайших игроков. Если у этого игрока плохое подключение, монстр странно движется на экранах у всех. - Почему → Следствие → На экране: Сервер поручает расчёт перемещения монстра клиенту ближайшего (или пришедшего первым) игрока → Отчёты этого игрока о результатах приходят на сервер с опозданием или пачками → Только этот монстр на экранах всех вокруг замирает и телепортируется. У самого игрока, которому поручен монстр, всё нормально - Симптомы: Телепортация, Микрофризы, Перемотка / Факторы: Джиттер, Потери - У кого: Странно выглядит один персонаж, Одна локация или канал / Когда: Всегда, Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Передавать управление игроку с хорошим подключением (по пингу и потерям), сразу забирать управление на сервер, если отчёты прекратились, важных монстров вроде боссов рассчитывать на сервере. - На графике: Высоко только у некоторых (интервал отчётов о позиции по монстрам (по клиентам, у которых управление)) - Где смотреть: Записывать на сервере для каждого монстра, у какого клиента управление, а также интервал отчётов, RTT и потери этого клиента. Интервалы прихода пакетов от этого клиента видны и в захвате пакетов на стороне сервера - Подтверждает: Управление всеми странно двигающимися монстрами у одного и того же игрока, его отчёты приходят неравномерно или прерываются, а после передачи управления другому игроку всё сразу приходит в норму - Опровергает: Если так же дёргаются и монстры, которых рассчитывает сам сервер, причина в задержке тика сервера или в подключении того, кто смотрит. Если после передачи управления рывки остаются, это расхождение расчёта пути при синхронизации команд - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Игрок, которому поручен монстр, никаких проблем не замечает, поэтому жалобы приходят только в виде «с монстром что-то не так». Если странное поведение одного и того же монстра видят все, кроме одного игрока, сначала проверяют, у кого управление этим монстром. - Источники: - [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) У персонажа, у которого накопились тысячи предметов и писем или необычно много друзей, записей в чёрном списке и баффов, данных для загрузки при входе, сохранения и рассылки окружающим в разы больше, чем у других. Тормозит только этот персонаж, независимо от подключения. - Почему → Следствие → На экране: У давно прокачиваемого персонажа в инвентаре и почте скопились тысячи предметов или наград за события → При каждом входе, переходе между локациями и сохранении приходится читать и писать в БД соответствующий объём, и данные о снаряжении и баффах для рассылки окружающим тоже большие → Только у этого персонажа долгая загрузка при входе и замирания при открытии инвентаря или почты. Если сервер ждёт сохранения в игровом потоке, на мгновение замирают и окружающие - Симптомы: Ошибка входа / бесконечная загрузка, Задержка ввода, Фриз / Факторы: Остановка - У кого: Только у меня, Только одна функция / Когда: Сразу после входа или техработ, При определённом действии, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Инфраструктура БД - Команда разработки, задачи: Ввести лимиты на хранение в инвентаре и почте и автоочистку старых писем, загружать данные частями по мере надобности, сохранять только изменения и вне игрового потока. - Команда инфраструктуры, задачи: Найти в логе медленных запросов повторяющиеся медленные чтения по одному и тому же персонажу и передать команде разработки, предоставить топ персонажей по числу строк предметов и писем. - Цифры для ориентира: Если один предмет занимает одну строку в БД, персонаж с 5 000 предметов при каждом входе читает 5 000 строк. Это в десятки раз больше, чем у обычного персонажа. - На графике: Высоко только у некоторых (время входа и сохранения по персонажам, число строк, читаемых из БД, по персонажам) - Где смотреть: Найти в логе медленных запросов БД (MySQL slow query log, PostgreSQL log_min_duration_statement) медленные чтения и сохранения, повторяющиеся с одним и тем же ID персонажа, и выгрузить топ персонажей по числу строк в таблицах предметов и писем - Подтверждает: Медленные запросы сосредоточены на нескольких ID персонажей, у этих персонажей строк предметов и писем в десятки раз больше среднего, и при входе с другого ПК и подключения они тормозят так же - Опровергает: Если вместе тормозят и другие персонажи того же аккаунта или другие игроки, причина в серверах БД или блокировках. Если на другом ПК с этим персонажем всё нормально, причина в окружении игрока - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Если тот же персонаж тормозит так же при входе с другого ПК и подключения, а другие персонажи того же аккаунта в порядке, подозревают данные персонажа. Поэтому в жалобе обязательно нужно имя персонажа. - Источники: - [Extraneous Fetching antipattern](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/extraneous-fetching/) · Microsoft Azure · Если извлекать больше данных, чем нужно, растёт нагрузка на ввод-вывод и замедляются ответы - [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 Если два персонажа находятся в разных каналах или инстансах или в разных «фазах», где набор видимых NPC зависит от прогресса квеста, они видят разные миры. - Почему → Следствие → На экране: Второй персонаж попал в другой канал или находится на другом этапе квеста → Этому персонажу сервер этот NPC не отправляет (это нормально) → NPC нет только у одного из них. Похоже на баг, но так задумано - Симптомы: Невидимки / фантомы / Факторы: Потери - У кого: Один из клиентов на одном ПК, Только у меня / Когда: Всегда, Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: отправлять клиенту данные о канале и фазе, добавить в чек-лист QA пункт «проверить канал и этап квеста обоих персонажей». Клиент: показывать на экране канал и фазу. - На графике: Высоко только у некоторых (число окружающих объектов по клиентам, канал и фаза) - Где смотреть: Сравнить в игре номера каналов обоих персонажей и этап нужного квеста, выровнять канал и этап и посмотреть снова. Если есть лог отправки объектов на сервере, проверить, по какой причине (канал, фаза) этот NPC не отправлялся этому персонажу - Подтверждает: Каналы или этапы квеста у двух персонажей разные, и после выравнивания NPC видно - Опровергает: Если канал и этап одинаковые, а NPC нет только у одного, смотреть причины: отбрасывание уведомлений о появлении во время загрузки, потеря данных о появлении из-за наплыва сразу после входа, сбой порядка регистрации в зоне видимости - Чем проверить: Проверка на стороне игрока - Подробнее: Стоит проверить и то, хранится ли прогресс квестов на уровне аккаунта или персонажа. Если это два персонажа одного аккаунта, прогресс одного может менять фазу другого. - Источники: - [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 не видно - Симптомы: Невидимки / фантомы / Факторы: Потери - У кого: Один из клиентов на одном ПК, Только у меня / Когда: Сразу после входа или техработ, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: по окончании загрузки отправлять сигнал «готов» или хранить пакеты, пришедшие во время загрузки, и обрабатывать их позже. Сервер: отправлять данные об окружении только после сигнала «готов». - Цифры для ориентира: Если на одном ПК два клиента загружаются одновременно или загружающийся клиент находится в фоновом окне, они делят CPU и диск, обработка ограничивается, и загрузка этого клиента может стать в разы дольше. Тот же баг проявляется и тогда, когда сервер начинает быстрее обрабатывать вход. - На графике: Высоко только у некоторых (время загрузки по клиентам, число сообщений, отброшенных во время загрузки) - Где смотреть: Сравнить число и типы сообщений, которые клиент получил и отбросил во время загрузки, и время окончания загрузки со временем отправки уведомлений о появлении на сервере. Легко воспроизвести, если на одном ПК загружать два клиента одновременно или держать загружающийся клиент в фоновом окне - Подтверждает: Сервер отправил уведомления о появлении невидимых NPC, они пришли до окончания загрузки, и в это время выросло число отброшенных сообщений. Бывает только у клиента с более долгой загрузкой - Опровергает: Если уведомления пришли после окончания загрузки, а 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, который уже ушёл - Симптомы: Невидимки / фантомы / Факторы: Потери - У кого: Один из клиентов на одном ПК, Только у меня / Когда: В движении и при смене локации, Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера - Команда разработки, задачи: Обновлять зону видимости в одном потоке и в одном порядке, периодически заново сверять весь «список видимых». - На графике: Случайные всплески (число расхождений между списком видимых на сервере и списком объектов на клиенте) - Где смотреть: Писать на сервере с номером тика регистрацию в сетке видимости, переходы объектов между ячейками и отправку уведомлений о появлении и исчезновении, периодически сравнивать «список видимых» на сервере со списком на клиенте - Подтверждает: Пропавший 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 Если сервер отправляет «только то, что изменилось с прошлого раза», то при потере первой полной посылки (опорного снапшота) последующие изменения применить невозможно. - Почему → Следствие → На экране: Пакет с полными данными объекта (опорный снапшот) теряется или отбрасывается до обработки → Клиенту не к чему применять последующие изменения, и он их игнорирует → Объект не виден или внезапно появляется спустя долгое время - Симптомы: Невидимки / фантомы, Телепортация / Факторы: Потери - У кого: Один из клиентов на одном ПК, Только у меня / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: обязательно повторять отправку опорного снапшота, пока не придёт подтверждение (ACK), формировать изменения только относительно опорного снапшота, получение которого клиент подтвердил. Клиент: отправлять подтверждение (ACK) опорного снапшота только после его фактического применения, при получении изменений для неизвестного объекта запрашивать у сервера данные заново. - На графике: Случайные всплески (число полученных изменений для неизвестных объектов) - Где смотреть: Сопоставить число отброшенных клиентом изменений без опорного снапшота и ID объектов со временем, когда сервер отправил опорный снапшот этого объекта и когда получил ACK. Воспроизвести в среде разработки, добавив потери (loss в tc netem, доля потерь пакетов в эмуляции сети Unreal) - Подтверждает: Для невидимого объекта сервер отправил опорный снапшот, ACK не получил, но продолжал слать только изменения, а клиент эти изменения отбрасывал - Опровергает: Если опорный снапшот подтверждён ACK и применён на клиенте, а объекта всё равно не видно, причина в потере уведомления об исчезновении или путанице из-за повторного использования ID объектов - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [Snapshot Compression](https://gafferongames.com/post/snapshot_compression/) · Gaffer On Games · Изменения нужно формировать только относительно опорного состояния (baseline), получение которого подтвердила другая сторона (ack), а начальное состояние отправлять отдельно - [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 и игроки остаются только на экране этого игрока. - Почему → Следствие → На экране: Уведомления о смерти, уходе или выходе из зоны видимости теряются или приходят не по порядку → Клиент считает, что объект всё ещё на месте → Монстр не реагирует на удары, на месте стоит игрок, который уже вышел из игры - Симптомы: Невидимки / фантомы / Факторы: Потери - У кого: Один из клиентов на одном ПК, Только у меня / Когда: Изредка, случайно, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: периодически отправлять «список видимых сейчас». Клиент: удалять объекты, которых нет в списке, скрывать объекты, которые должны двигаться, но долго не обновлялись. - На графике: Случайные всплески (число объектов, оставшихся только на клиенте) - Где смотреть: Сравнить «список видимых сейчас» от сервера со списком объектов на клиенте, посчитать объекты, которые есть только на клиенте, и сопоставить по 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) или если буфер приёма переполнится, пока клиент занят загрузкой и не читает сокет, часть данных пропадает и повторно не приходит. - Почему → Следствие → На экране: Сразу после входа данные о появлении приходят плотной пачкой за короткое время → Клиент во время загрузки поздно читает сокет, и буфер приёма ОС переполняется, или большой UDP-пакет фрагментируется, и при потере одного фрагмента пропадает целиком. По ненадёжному каналу доставки повторной отправки нет → Только в клиенте с медленной загрузкой не хватает нескольких NPC. Если выйти из зоны видимости и вернуться, они видны - Симптомы: Невидимки / фантомы / Факторы: Потери - У кого: Один из клиентов на одном ПК, Только у меня / Когда: Сразу после входа или техработ, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: уведомления о появлении и исчезновении отправлять только по надёжному каналу с гарантированной повторной передачей, начальные данные отправлять частями. Клиент: принимать данные в отдельном от загрузки потоке, увеличить буфер приёма. - Цифры для ориентира: Стандартный размер буфера приёма UDP на ПК зависит от ОС, но обычно составляет от десятков до сотен KB. Если данных о входе в людный город больше, буфер переполняется, даже если загрузка лишь ненадолго мешает читать сокет. - На графике: Всплеск сразу после входа или техработ (объём приёма сразу после входа, число пропущенных уведомлений о появлении) - Где смотреть: Сравнить число уведомлений о появлении, отправленных сервером сразу после входа, с числом полученных клиентом и посмотреть, по какому каналу (надёжному или ненадёжному) они шли. В захвате пакетов на стороне сервера смотреть объём, ушедший этому игроку сразу после входа, и фрагментированные пакеты (фильтр Wireshark ip.flags.mf == 1 || ip.frag_offset > 0) - Подтверждает: Получено меньше, чем отправлено, пропуски собраны на участке плотной пачки сразу после входа, а отправка шла по ненадёжному каналу или большие пакеты фрагментированы. Чаще бывает в клиенте с медленной загрузкой - Опровергает: Если отправлено и получено одинаково, а NPC не видно, данные отброшены после получения (отбрасывание уведомлений о появлении во время загрузки) или проблема в расчёте зоны видимости. Если пропуски бывают в любое время, независимо от входа, это потери на линии связи - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [RFC 8085: UDP Usage Guidelines](https://www.rfc-editor.org/rfc/rfc8085) · IETF · Фрагментированный пакет пропадает целиком при потере любого одного фрагмента - [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 · Стандартный размер буфера приёма сокета зависит от ОС - [Display Filter Reference: Internet Protocol Version 4](https://www.wireshark.org/docs/dfref/i/ip.html) · Wireshark · Фрагментированные IP-пакеты отбираются по ip.flags.mf (More fragments) и ip.frag_offset (Fragment Offset) #### pt-id-reuse · Путаница из-за повторного использования ID объектов · Entity ID reused without a generation counter Если при возрождении убитого NPC сервер снова использует тот же ID объекта, клиент, пропустивший за это время уведомление об исчезновении, принимает новый NPC за старый. - Почему → Следствие → На экране: NPC умирает и появляется снова с тем же ID объекта → Клиент, пропустивший уведомление об исчезновении, считает объект «уже известным» и игнорирует уведомление о появлении или оставляет объект мёртвым → NPC нет только на одном экране или он лежит убитым, иногда выглядит как другой NPC - Симптомы: Невидимки / фантомы / Факторы: Потери - У кого: Один из клиентов на одном ПК, Только у меня / Когда: Изредка, случайно, При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: добавлять к ID объекта номер поколения, чтобы различать повторное использование. Клиент: получив уведомление о появлении для уже известного ID, удалять старый объект и создавать новый. - На графике: Случайные всплески (число уведомлений о появлении для уже известных ID) - Где смотреть: Писать на сервере время создания и удаления по каждому ID объекта (и номер поколения, если он есть), считать, сколько раз клиент получал уведомление о появлении для уже известного ID и сколько удалений с повторным созданием при обновлении зоны видимости прошли как «без изменений» - Подтверждает: ID невидимого или лежащего NPC совпадает с ID NPC, который только что умер, и за это время клиент не получил уведомление об исчезновении или сервер не отправил ни уведомление об исчезновении, ни уведомление о появлении - Опровергает: Если у ID есть номер поколения и он участвует в сравнении, причина не эта. Если ID не переиспользован, а объекта всё равно не видно, причина в потере уведомления о появлении - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Подробнее: Это бывает и на стороне сервера. Если сравнивать список объектов в зоне видимости только по ID, NPC, который между двумя обновлениями зоны видимости умер и возродился с тем же ID, считается «без изменений», и не отправляется ни уведомление об исчезновении, ни уведомление о появлении. Если обновления зоны видимости у разных игроков происходят в разные моменты, проблема бывает только у клиентов, чьё обновление пришлось на этот момент. - Источники: - [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 Если клиент рассчитан на конкретный локальный порт, второй клиент на том же ПК не может занять порт или делит пакеты с первым. - Почему → Следствие → На экране: Два клиента пытаются открыть один и тот же локальный UDP-порт (принудительно делят его через опцию повторного использования) → ОС передаёт входящие пакеты только одному из сокетов или не гарантирует, какой из них получит пакет. Роутер и сервер тоже видят оба клиента под одним адресом → Один клиент не получает пакеты мира: не видно NPC и других игроков, или случается дисконнект - Симптомы: Невидимки / фантомы, Дисконнект, Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Один из клиентов на одном ПК / Когда: Сразу после входа или техработ, Всегда - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: поручить выбор локального порта ОС (bind на порт 0). Сервер: различать соединения по сессионному токену, выданному каждому соединению. - На графике: Высоко только у некоторых (число принятых пакетов по клиентам) - Где смотреть: На ПК игрока с двумя запущенными клиентами выполнить в командной строке netstat -ano -p udp и посмотреть, какие локальные UDP-порты открыл каждый игровой процесс (PID). На стороне сервера проверить, приходят ли две сессии с одного публичного IP и одного порта - Подтверждает: Два игровых процесса привязаны к одному локальному порту, или на сервере две сессии видны с одного IP и порта. С одним запущенным клиентом всё нормально - Опровергает: Если клиенты используют разные локальные порты, а с одним из них всё равно что-то не так, причина в ошибке разделения сессий по IP или устройству либо в ограничении на несколько клиентов - Чем проверить: Проверка на стороне игрока - Источники: - [Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE](https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse) · Microsoft · При повторном bind на тот же порт с SO_REUSEADDR второй сокет перехватывает порт, и неизвестно, какой сокет получит пакет - [bind function (winsock.h)](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind) · Microsoft · При bind на порт 0 выделяется уникальный порт из динамического диапазона (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 устройства, два клиента на одном ПК (с одним публичным IP) считаются одним игроком. - Почему → Следствие → На экране: Таблица сессий строится по IP или по IP + ID устройства → Данные второго клиента перезаписывают первую сессию или смешиваются с ней → В одном клиенте не видно NPC, а в другом случается дисконнект или приходят чужие данные - Симптомы: Невидимки / фантомы, Дисконнект / Факторы: Потери - У кого: Один из клиентов на одном ПК, Все в одном доме, Один регион или провайдер / Когда: Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: и на сервере, и на промежуточных серверах различать соединения по уникальному сессионному токену; исправить обязательно, потому что та же проблема бывает у нескольких игроков из одного дома (за NAT роутера) и у абонентов мобильных сетей, где оператор выдаёт один IP многим абонентам (CGNAT). Клиент: использовать отдельный сессионный токен для каждого запущенного клиента. - На графике: Высоко только у некоторых (число одновременных сессий с одного публичного IP, число перезаписей сессий) - Где смотреть: Писать в логи сервера и промежуточного сервера ключ поиска сессии, сессионный токен, IP и порт клиента и смотреть, менялась ли существующая сессия в момент второго подключения с того же IP. Воспроизводится, если запустить по очереди два клиента на одном ПК - Подтверждает: В момент подключения второго клиента меняются адрес или данные персонажа в первой сессии, и такие же дисконнекты видны у других игроков за тем же роутером или мобильным подключением (CGNAT) - Опровергает: Если две сессии с одного IP держатся раздельно с разными токенами, причина не эта. Если два процесса используют один локальный порт, это конфликт фиксированного UDP-порта - Чем проверить: Нужны логи и метрики игрового сервера или клиента - Источники: - [RFC 6269: Issues with IP Address Sharing](https://www.rfc-editor.org/rfc/rfc6269) · IETF · Когда несколько абонентов делят один IPv4-адрес через NAT или CGN, различить пользователей только по IP невозможно #### pt-multiclient · Ограничение на несколько клиентов · Multi-client restriction policy Если модуль защиты или политика сервера ограничивает число клиентов на одном ПК, второй клиент не запускается или не подключается, либо у первого случается дисконнект. Некоторые игры блокируют только функции дополнительного клиента. - Почему → Следствие → На экране: Модуль защиты обнаруживает повторный запуск, или сервер ограничивает дополнительные подключения с одного устройства → Второй запуск или подключение отклоняется, либо один из клиентов отключается. Изредка блокируется только часть функций дополнительного клиента → Ошибка входа или дисконнект у одного клиента. В играх, которые блокируют только функции, у одного клиента не видно NPC или магазина - Симптомы: Ошибка входа / бесконечная загрузка, Невидимки / фантомы, Дисконнект / Факторы: Потери - У кого: Один из клиентов на одном ПК / Когда: Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: если ограничивать, то с понятным сообщением, сделать в модуле защиты исключение для QA. Сервер: в ограничении подключений с одного устройства тоже сделать исключение для QA. - На графике: Высоко только у некоторых (число отказов в подключении и обрывов по причинам (повторное подключение)) - Где смотреть: Посмотреть сообщение при запуске второго клиента и сообщение об обрыве у первого. Проверить, пишутся ли в логи отказов и киков на сервере коды причин вроде «повторное подключение» или «то же устройство» - Подтверждает: В момент второго запуска или подключения появляется сообщение об отказе или первый клиент отключается с причиной «повторное подключение», а с одним клиентом проблем нет - Опровергает: Если оба клиента подключаются без отказов и обрывов, а 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 Для клиента в фоновом окне игра, движок и ОС снижают частоту кадров и объём обработки. Полученные пакеты не успевают обрабатываться, копятся и переполняют буфер. - Почему → Следствие → На экране: Ограничение кадров в фоне в настройках игры или графического драйвера (например, в драйвере NVIDIA задаётся от 20 до 200 в секунду), энергосбережение, настройка движка останавливаться в фоне. ОС тоже отдаёт приоритет по CPU и GPU окну на переднем плане → За кадр обрабатывается меньше пакетов, очередь растёт, а при переполнении буфера приёма пакеты отбрасываются → Когда окно выводят на передний план, всё появляется разом, или некоторые NPC так и не появляются - Симптомы: Невидимки / фантомы, Перемотка, Дисконнект / Факторы: Остановка, Потери - У кого: Один из клиентов на одном ПК / Когда: После бездействия, Всегда - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Внешние стороны · Внешние стороны - Команда разработки, задачи: Продолжать приём сетевых данных в отдельном от игрового цикла потоке, гарантировать минимальный объём обработки и в фоне, включить в движке выполнение в фоне (в Unity это runInBackground). - Внешние стороны, задачи: Посоветовать игрокам отключить ограничение кадров в фоне в графическом драйвере и режим энергосбережения ПК. - Цифры для ориентира: В Unity при выключенном runInBackground игровой цикл останавливается в момент, когда окно теряет фокус. Если приём идёт только в этом цикле, всё это время пакеты вообще не обрабатываются. - На графике: Провал, затем пачка (интервал между кадрами клиента, число обработанных пакетов за кадр) - Где смотреть: На одном ПК держать одно окно на переднем плане, а другое на заднем, менять их местами и сравнивать. Измерить в PresentMon интервал между кадрами обоих процессов, а если есть игровые логи, смотреть состояние фокуса окна и число обработанных пакетов за кадр - Подтверждает: Только в фоновом окне интервал между кадрами сильно растёт (при ограничении в драйвере выходит на плато на интервале, соответствующем заданной частоте кадров) или обработка останавливается, а если поменять окна местами, проблема переходит к другому клиенту - Опровергает: Если то же самое бывает и в окне на переднем плане, фоновые ограничения ни при чём. Если независимо от положения окна сбоит всегда один и тот же клиент, причина в разных настройках отображения или в несовпадении версий - Чем проверить: Проверка на стороне игрока - Источники: - [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 · Инструмент, который собирает время кадра на CPU, GPU и дисплее для каждого графического приложения Windows #### pt-asset-lock · Конфликт одновременного доступа к файлам кэша и ассетов · Shared cache / asset file lock conflicts Если два клиента одновременно пишут в одну папку кэша или блокируют файлы, один из них не может загрузить модели и текстуры NPC. - Почему → Следствие → На экране: Два клиента одновременно пишут файлы кэша и патчей в одной папке установки → Не удаётся заблокировать файл или читается наполовину записанный файл, и загрузка срывается → Табличка с именем видна, а модели персонажа нет, или NPC прозрачный - Симптомы: Невидимки / фантомы / Факторы: Остановка - У кого: Один из клиентов на одном ПК / Когда: Сразу после входа или техработ, В движении и при смене локации - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Сделать отдельную папку кэша для каждого клиента, повторять попытку при неудачной блокировке файла, при неудачной загрузке показывать хотя бы модель по умолчанию. - На графике: Высоко только у некоторых (число неудачных загрузок ассетов по клиентам) - Где смотреть: На ПК игрока отфильтровать в Process Monitor только пути к папкам установки и кэша игры и смотреть результаты открытия и записи файлов обоими игровыми процессами. Если есть лог клиента, искать неудачные загрузки ассетов и код ошибки открытия файла (ERROR_SHARING_VIOLATION) - Подтверждает: Открытие файла невидимой модели завершилось нарушением общего доступа или ошибкой блокировки, и в это же время другой клиент писал в этот файл. С одним клиентом или с раздельными папками установки и кэша проблема пропадает - Опровергает: Если та же модель не видна и с одним запущенным клиентом, причина в повреждении файла или несовпадении версии клиента или данных. Если файлы открываются нормально, а объект не рисуется, это нехватка памяти или 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 Когда два клиента делят видеопамять, для новых моделей и текстур не остаётся места, и часть объектов не рисуется. - Почему → Следствие → На экране: Два клиента делят VRAM и RAM. Кроме того, ОС может в первую очередь урезать квоту видеопамяти фонового окна → Движок не может загрузить новые модели и текстуры или постоянно выгружает и загружает их снова → NPC появляются с опозданием, размыты или не видны, микрофризы - Симптомы: Невидимки / фантомы, Микрофризы / Факторы: Остановка - У кого: Один из клиентов на одном ПК / Когда: В движении и при смене локации, При наплыве игроков - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Внешние стороны · Внешние стороны - Команда разработки, задачи: Автоматически подстраивать качество под бюджет памяти, при неудачной загрузке показывать замещающую модель. - Внешние стороны, задачи: Игрокам, которые запускают два клиента одновременно, посоветовать снизить качество графики или включить режим для слабых ПК, сообщить рекомендуемые требования к VRAM и RAM. - На графике: Упор в лимит (плато) (использование выделенной памяти GPU по процессам) - Где смотреть: На ПК игрока добавить на вкладке «Подробности» Диспетчера задач столбец выделенной памяти GPU и сравнить суммарное использование двух клиентов с объёмом VRAM видеокарты. В игре записывать бюджет (Budget) и текущее использование (CurrentUsage), которые сообщает QueryVideoMemoryInfo в DXGI - Подтверждает: Суммарное использование двух клиентов выходит на плато около объёма VRAM, а неудачные загрузки моделей и текстур приходятся на моменты, когда текущее использование превышает бюджет. При снижении качества или с одним клиентом проблема пропадает - Опровергает: Если 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 (бюджет видеопамяти, заданный ОС) и CurrentUsage (текущее использование приложением). Если использование превышает бюджет, возможны рывки #### pt-display-option · Разные настройки отображения · Different display settings Если у двух клиентов различаются настройки вроде лимита отображаемых персонажей, скрытия табличек с именами и моделей NPC или режима для слабых ПК, они показывают разное. - Почему → Следствие → На экране: Только в одном клиенте включён «лимит числа отображаемых персонажей» или режим для слабых ПК → Дальние или низкоприоритетные NPC не рисуются (это нормально) → NPC нет только в одном клиенте - Симптомы: Невидимки / фантомы / Факторы: Остановка - У кого: Один из клиентов на одном ПК, Только у меня / Когда: При наплыве игроков, Всегда - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Показывать, что объект скрыт настройками, разделить файлы настроек по клиентам, чтобы они не смешивались. - На графике: Высоко только у некоторых (число отрисованных на экране объектов по клиентам) - Где смотреть: Сравнить у двух клиентов лимит отображаемых персонажей, скрытие табличек с именами и моделей и режим для слабых ПК, выставить в одном клиенте те же значения, что в другом. Проверить также, не пишут ли клиенты в один общий файл настроек, перезаписывая изменения друг друга - Подтверждает: При одинаковых настройках экраны совпадают, а невидимые NPC оказываются дальними объектами за пределами лимита отображения или объектами с низким приоритетом - Опровергает: Если и при одинаковых настройках 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 Если второй клиент установлен отдельно или не до конца пропатчен, он не знает новых ID NPC от сервера и молча их игнорирует. - Почему → Следствие → На экране: Установка в другой папке или клиент, запущенный во время патча → Получив неизвестный ID NPC или модели, клиент пропускает его → В одном клиенте не видно только недавно добавленных NPC - Симптомы: Невидимки / фантомы / Факторы: Потери - У кого: Один из клиентов на одном ПК / Когда: Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Клиент: отправлять при подключении версию данных, при получении неизвестного ID писать лог и показывать замену. Сервер: проверять версию данных при подключении и при несовпадении отказывать в подключении и предлагать обновиться. - На графике: Высоко только у некоторых (число полученных неизвестных ID по версиям клиента) - Где смотреть: Сравнить пути к исполняемым файлам двух клиентов и версии клиента и данных, которые видны на экране и в логах. В игре записывать версию данных, отправленную при подключении, и сколько раз неизвестные ID NPC и моделей были получены и пропущены - Подтверждает: У двух клиентов разные версии или папки установки, невидимые NPC добавлены последним патчем, а в полностью обновлённой установке они видны - Опровергает: Если версии и папки установки одинаковые, а 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 появляются поздно или не появляются только в одном клиенте - Симптомы: Невидимки / фантомы, Задержка ввода / Факторы: Задержка - У кого: Один из клиентов на одном ПК, Одна локация или канал / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: повышать приоритет отложенных объектов со временем (защита от starvation), гарантировать минимальную частоту обновления. Клиент: вовремя отправлять подтверждения приёма и в фоне, чтобы оценка пропускной способности не занижалась. - На графике: Растёт вслед за онлайном и нагрузкой (число отложенных объектов по соединениям, объём отправки по соединениям) - Где смотреть: Писать на сервере для каждого соединения число отправленных байтов за тик, лимит отправки (оценку пропускной способности), число отложенных и неотправленных объектов и время с последней отправки каждого объекта. В 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 Если клиент неверно оценивает серверное время, только что пришедшие данные объекта он откладывает как «ещё из будущего» или отбрасывает как «слишком старые». - Почему → Следствие → На экране: Оценка серверного времени у одного клиента сильно сбита (измерение во время загрузки, выход из режима сна) → Время, на которое ведётся интерполяция, не совпадает со временем в данных объекта → Объект появляется с опозданием или стоит неподвижно - Симптомы: Невидимки / фантомы, Микрофризы / Факторы: Задержка - У кого: Один из клиентов на одном ПК / Когда: После бездействия, Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка клиента - Команда разработки, задачи: Периодически заново синхронизировать время и при большом расхождении сразу сбрасывать оценку, не использовать значения, измеренные во время загрузки или сразу после выхода из сна. - На графике: Высоко только у некоторых (ошибка оценки серверного времени по клиентам) - Где смотреть: Записывать на клиенте оценку серверного времени, 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 (потери на пути ввода игрока к серверу восстанавливает ОС клиента), при серии потерь или скачке пинга показывать на экране состояние сети. - Команда инфраструктуры, задачи: Ускорить восстановление потерь с помощью RACK-TLP (предотвратить потери на беспроводном участке сервер не может, он может только быстрее их восстанавливать), проверить, что не изменены значения по умолчанию современных Linux net.ipv4.tcp_recovery=1 (RACK) и net.ipv4.tcp_early_retrans=3 (TLP). - Внешние стороны, задачи: Посоветовать игроку кабельное подключение, диапазоны 5 GHz или 6 GHz, другое место для роутера или другой радиоканал. - Цифры для ориентира: При потерях 1% на беспроводном участке пропадает 1 игровой пакет из 100. Если за 1 с приходит 10 пакетов, короткий фриз случается примерно раз в 10 с. Без RACK-TLP каждый такой фриз длится RTO (пинг + 200 ms и больше). - На графике: Высоко только у некоторых (доля повторных передач по соединениям, RTT (пинг) по соединениям) - Где смотреть: С ПК игрока по несколько сотен раз пинговать адрес роутера (шлюза) и игровой сервер и сравнить потери и разброс задержки, затем переключиться на кабель или мобильный интернет и измерить снова. На сервере посмотреть в 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 отключает алгоритм Нейгла, и даже мелкие данные отправляются сразу - [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 = 200 ms - [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). Даже не выброшенные пакеты ждут в конце забитой очереди → Разом пропадает несколько пакетов: долгий фриз, потом перемотка, чаще по вечерам - Симптомы: Фриз, Перемотка, Откидывание назад / Факторы: Потери, Задержка - У кого: Все в одном доме, Один регион или провайдер, Весь сервер / Когда: Вечерний пик, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Внешние стороны · Внешние стороны, Команда разработки · Разработка клиента - Команда разработки, задачи: При серии потерь или скачке пинга показывать на экране состояние сети (с подсказкой, что через это же подключение, возможно, идёт большая загрузка). - Команда инфраструктуры, задачи: Обеспечить запас пропускной способности на линиях связи ЦОД, проверять счётчики отбрасываний в очереди (output drops) на наших линиях и портах коммутаторов, если забит участок провайдера, обходить его через другие линии или пиринг. - Внешние стороны, задачи: Посоветовать игрокам SQM на роутере (fq_codel, CAKE) и ECN (скорость снижается раньше, чем очередь переполнится), попросить провайдера расширить узкое место. - Цифры для ориентира: В момент переполнения очереди в течение десятков ms разом пропадает значительная часть входящих пакетов. Пакеты теряются подряд, легко теряется и повторная передача, поэтому дело часто доходит до RTO. - На графике: Высоко только в определённые часы (доля повторных передач, RTT (пинг)) - Где смотреть: Разбить долю повторных передач на сервере (прирост TcpRetransSegs ÷ TcpOutSegs по nstat, запускаемому раз в минуту) и 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 держат очередь короткой и уменьшают bufferbloat - [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: SQM для роутеров, объединяющий шейпер и управление очередью семейства fq_codel - [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 · TcpRetransSegs и TcpOutSegs в выводе nstat (RetransSegs и OutSegs в разделе Tcp) - [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 Когда сервер каждый тик разом выплёскивает обновления для тысяч игроков, маленький буфер коммутатора или мгновенный лимит облака переполняется меньше чем за 1 ms, и часть пакетов выбрасывается. - Почему → Следствие → На экране: В начале тика пакеты для всех игроков отправляются разом → Мгновенно переполняется буфер порта коммутатора, где сходится трафик нескольких серверов (от сотен KB до нескольких MB на порт), или лимит облачного инстанса (средняя загрузка при этом низкая) → Телепортация и короткие фризы сразу у многих игроков, по средним метрикам причину не видно - Симптомы: Телепортация, Фриз, Перемотка / Факторы: Потери - У кого: Одна локация или канал, Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда инфраструктуры · Сетевая инфраструктура - Команда разработки, задачи: Распределять отправку одного тика по всему тику (наплыв тысяч соединений в начале тика пейсинг по соединениям почти не сглаживает), разносить момент начала тика по серверам, для соединений с большими объёмами ограничивать скорость через SO_MAX_PACING_RATE. - Команда инфраструктуры, задачи: Серверы и ОС: ограничить общую скорость отправки сервера (шейпер в ОС сервера, tc в Linux), выплески одного соединения выравнивать пейсингом (очередь fq в Linux, BBR). Сеть: коммутаторы с большими буферами, проверять счётчики выходных отбрасываний на портах коммутаторов с коротким интервалом (по средней загрузке их не видно). - Цифры для ориентира: Порт 10 Gbps за 1 ms отправляет около 1,25 MB. Если тики нескольких серверов совпадают и их трафик сходится в один порт, буфер заполняется мгновенно. - На графике: Растёт вслед за онлайном и нагрузкой (выходные отбрасывания на портах коммутатора, доля повторных передач) - Где смотреть: С интервалом в несколько секунд собирать выходные отбрасывания (ifOutDiscards) на порту коммутатора, к которому подключён сервер, и на порту уровнем выше, в облаке смотреть bw_out_allowance_exceeded и pps_allowance_exceeded в ethtool -S. Собрать повторные передачи за то же время через bcc tcpretrans и сопоставить - Подтверждает: Средняя загрузка по минутам низкая, но растут выходные отбрасывания или превышения allowance, и их число растёт вместе с онлайном и числом игроков в одном месте. Повторные передачи возникают в один и тот же момент на многих соединениях этого сервера и не привязаны к диапазонам IP определённых игроков (провайдер, регион) - Опровергает: Если на том же порту растут и ошибки CRC и ошибки приёма, это «Физические ошибки (неисправные кабели, оптические модули и разъёмы)». Если на принимающем сервере растут счётчики отбрасываний NIC или softnet dropped, это «Отбрасывание пакетов на принимающем сервере» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Пейсинг работает для каждого соединения отдельно. Наплыв, когда тысячи соединений в начале тика отправляют по одному-два пакета, пейсинг по соединениям почти не сглаживает, и игровому серверу приходится самому распределять моменты отправки. Зато когда одно соединение отправляет большой объём, 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 под скорость потока (максимум 64 KB, 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 · Для каждой повторной передачи выводит строку с адресом и портом другой стороны и состоянием соединения #### rt-policer · Отбрасывание избытка полисером · Traffic policing Тарифы провайдеров, лимиты облачных инстансов и оборудование защиты от DDoS могут сразу отбрасывать пакеты сверх заданной скорости, не ставя их в очередь. - Почему → Следствие → На экране: Мгновенный объём передачи превышает разрешённую скорость или разрешённый всплеск (burst) → Пакеты сверх лимита отбрасываются сразу, без очереди (полисинг) → В моменты больших всплесков пропадает сразу несколько пакетов: фриз, потом перемотка. Средняя скорость при этом ниже лимита - Симптомы: Фриз, Перемотка, Телепортация / Факторы: Потери - У кого: Весь сервер, Один регион или провайдер, Только у меня / Когда: При наплыве игроков, Вечерний пик - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка сервера - Команда разработки, задачи: Распределять объём, который выплёскивается каждый тик, по всему тику, чтобы мгновенный объём передачи был ниже разрешённого всплеска, при упоре в лимит пакетов в секунду объединять сообщения одного тика в один пакет. - Команда инфраструктуры, задачи: Сеть: проверить счётчики превышения полисера на оборудовании, заменить полисер шейпером, увеличить разрешённый всплеск. Серверы и ОС: проверить метрики превышения облачных лимитов (в AWS bw_out_allowance_exceeded и pps_allowance_exceeded в ethtool -S), перейти на инстанс помощнее, включить пейсинг на сервере (очередь fq в Linux). - Цифры для ориентира: Шейпер (ставит пакеты в очередь и задерживает) увеличивает задержку, полисер (сразу отбрасывает) увеличивает потери. Игровое TCP-соединение от одной потери может встать на сотни ms, поэтому при кратком превышении лимита обычно сильнее влияет полисер. - На графике: Упор в лимит (плато) (объём отправки с коротким интервалом, счётчики превышения полисера и allowance) - Где смотреть: Посмотреть счётчики превышения (exceed) и отбрасываний на оборудовании с полисером, в облаке bw_out_allowance_exceeded и pps_allowance_exceeded в ethtool -S. Для соединений с потерями посмотреть RTT непосредственно перед потерей по rtt в ss -ti или по захвату пакетов - Подтверждает: Счётчики превышения растут, а объём отправки на коротком интервале плоский, будто срезан на каком-то значении. Перед потерями 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 · bw_out_allowance_exceeded и pps_allowance_exceeded в ethtool -S: число пакетов, поставленных в очередь или отброшенных из-за превышения лимитов инстанса - [tc-fq(8) — Linux manual page](https://man7.org/linux/man-pages/man8/tc-fq.8.html) · iproute2 · Пейсинг по соединениям в очереди fq в Linux - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rtt (среднее время пути туда и обратно) в ss -i #### rt-physical · Физические ошибки (неисправные кабели, оптические модули и разъёмы) · Bit errors: bad cable, optics, dirty fiber Повреждённый кабель, запылённый оптический разъём или выработавший ресурс оптический модуль дают битовые ошибки, и оборудование молча выбрасывает испорченные пакеты. - Почему → Следствие → На экране: Из-за неисправного кабеля, оптического модуля или разъёма переворачиваются биты → Оборудование отбрасывает пакеты с неверной контрольной суммой (CRC) → Только у тех, чей трафик идёт по этому пути, постоянно бывают короткие фризы с перемоткой, время суток не влияет - Симптомы: Фриз, Перемотка, Телепортация / Факторы: Потери - У кого: Одна локация или канал, Все в одном доме / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура, Внешние стороны · Внешние стороны - Команда инфраструктуры, задачи: Ошибки CRC копятся на принимающей стороне того направления, где повреждаются пакеты, поэтому проверять оба конца. Сеть: проверить счётчики CRC и ошибок приёма на портах оборудования, уровень оптического сигнала (информация об оптических модулях на коммутаторе), почистить оптические разъёмы, заменить кабель или оптический модуль. Серверы и ОС: проверить rx_crc_errors в ethtool -S на сервере (название у разных драйверов немного отличается) и уровень оптического сигнала (ethtool -m), заменить кабель или NIC на стороне сервера. - Внешние стороны, задачи: Если проблема на участке в доме игрока, посоветовать заменить сетевой кабель или роутер, если на линии провайдера, попросить провайдера проверить линию. - Цифры для ориентира: Даже 0,1% потерь означает один пакет из 1 000 игровых. Если по этому пути идёт трафик десятков игроков, кто-то ловит короткий фриз каждые несколько секунд. Битовые ошибки чаще задевают большие пакеты. - На графике: Высоко только у некоторых (число ошибок CRC по портам, доля повторных передач по серверам и портам) - Где смотреть: Посмотреть счётчики CRC на обоих концах линка. На сервере rx_crc_errors в ethtool -S или crc в ip -s -s link, на коммутаторе ошибки FCS порта (dot3StatsFCSErrors) и ошибки приёма (ifInErrors). Для оптического линка посмотреть уровень принимаемого оптического сигнала через ethtool -m и в информации об оптических модулях на коммутаторе - Подтверждает: Ошибки 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 показывает EEPROM оптического модуля (SFP+, QSFP) и данные оптической диагностики - [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) на полнодуплексной. Серверы и ОС: проверить скорость и дуплекс через ethtool. - Цифры для ориентира: Для 1 Gbps по меди автосогласование обязательно, а на 10 Gbps и выше полудуплекса нет вообще. Поэтому сейчас проблема встречается в основном на старом оборудовании 100 Mbps и медленнее, на портах управления и на некоторых стыках линий связи. - На графике: Растёт вслед за онлайном и нагрузкой (поздние коллизии и ошибки CRC на порту, доля повторных передач) - Где смотреть: Посмотреть фактические скорость и дуплекс на обеих сторонах линка. На сервере ethtool, запущенный только с именем интерфейса, на коммутаторе состояние порта или dot3StatsDuplexStatus по SNMP. Заодно посмотреть поздние коллизии (на сервере tx_window_errors, на коммутаторе dot3StatsLateCollisions) и ошибки CRC - Подтверждает: Одна сторона показывает полудуплекс, другая полный дуплекс. При каждом росте трафика на полудуплексной стороне растут поздние коллизии, а на полнодуплексной ошибки CRC - Опровергает: Если скорость и дуплекс на обеих сторонах совпадают, а растут только CRC, это «Физические ошибки (неисправные кабели, оптические модули и разъёмы)». На линках 10 Gbps и выше полудуплекса нет, поэтому для них эта причина исключается - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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-гигабитный Ethernet поддерживает только полный дуплекс - [IEEE P802.3ba Objectives](https://www.ieee802.org/3/ba/PAR/P802.3ba_Objectives_0709.pdf) · IEEE · 40- и 100-гигабитный Ethernet тоже поддерживают только полный дуплекс - [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 · speed, duplex и autoneg в ethtool -s задают скорость, дуплекс и автосогласование, а с одним только именем интерфейса ethtool показывает текущие настройки - [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 (буфер, где ненадолго лежат пришедшие пакеты) или загружено до предела процессорное ядро, на котором ОС обрабатывает приём. - Почему → Следствие → На экране: Резкий наплыв игроков, все прерывания на одном ядре CPU, CPU steal на виртуальной машине, перегрузка виртуального коммутатора → Отбрасывание в кольцевом буфере (rx_missed_errors и др., название зависит от драйвера) или в очереди приёма ядра (softnet dropped) → Когда игроков много, на всём сервере одновременно задержка ввода и короткие фризы - Симптомы: Задержка ввода, Фриз, Перемотка, Телепортация / Факторы: Потери, Остановка - У кого: Весь сервер / Когда: При наплыве игроков - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Добавить в мониторинг счётчики отбрасываний вроде rx_missed_errors из ethtool -S и dropped из /proc/net/softnet_stat, увеличить кольцевой буфер (ethtool -G), распределить RSS и прерывания по нескольким ядрам, разнести игровые потоки и ядра обработки приёма, держать запас CPU, на виртуальной машине проверить CPU steal и нагрузку виртуального коммутатора. - Цифры для ориентира: Пакеты, которые сервер получил и выбросил, повторно отправляет клиент. Поэтому в метриках повторных передач сервера проблема почти не видна и сначала проявляется в счётчиках отбрасываний вроде rx_missed_errors в ethtool -S (название зависит от драйвера) и в dropped из /proc/net/softnet_stat. - На графике: Упор в лимит (плато) (загрузка softirq по ядрам, счётчики отбрасываний NIC) - Где смотреть: Посмотреть счётчики отбрасываний в ethtool -S (rx_missed_errors и др., у mlx5 rx_out_of_buffer и rx_discards_phy), missed в ip -s -s link, 2-й столбец (dropped) и 3-й столбец (time_squeeze) в /proc/net/softnet_stat, а через mpstat -P ALL посмотреть %soft (обработка программных прерываний) по ядрам. На виртуальной машине смотреть и %steal - Подтверждает: В часы наплыва игроков растут счётчики отбрасываний или softnet dropped, а %soft на ядре, обрабатывающем приём, упирается почти в 100% и выше не поднимается. На всех соединениях этого сервера одновременно запаздывает ввод - Опровергает: Если счётчики отбрасываний на сервере не меняются, а повторные передачи сосредоточены на соединениях определённого региона или провайдера, это потери на маршруте. Когда на маршруте теряются пакеты, отправленные сервером, растёт TcpRetransSegs в nstat на сервере, а эти счётчики не меняются - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 задаёт драйвер (например, rx_missed_errors и rx_no_buffer_count у igb) - [Ethtool counters](https://docs.kernel.org/networking/device_drivers/ethernet/mellanox/mlx5/counters.html) · Linux kernel · rx_out_of_buffer (нет буфера в очереди приёма) и rx_discards_phy (отброшено из-за нехватки буфера порта) драйвера mlx5 - [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, значения шестнадцатеричные, 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 · steal в /proc/stat: время, когда 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 · TcpRetransSegs в выводе nstat (RetransSegs в разделе Tcp) #### rt-stateful-fw · Отбрасывание пакетов файрволом или conntrack · Stateful firewall / conntrack drops Файрвол или conntrack в Linux (функция, которая записывает проходящие соединения в таблицу) выбрасывает пакеты, если таблица заполнена или состояние соединения кажется ему неверным. - Почему → Следствие → На экране: Таблица отслеживания соединений заполнена (table full), или пакеты туда и обратно идут разными путями, и через файрвол проходит только одно направление (асимметричный маршрут) → Файрвол считает, что пакет относится к «неизвестному соединению» или несёт «номер последовательности вне окна», и отбрасывает его → Когда таблица заполнена, новые подключения не проходят, а при расхождении маршрутов у игроков на этом маршруте после серии повторных передач дисконнект - Симптомы: Фриз, Дисконнект, Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Весь сервер, Один регион или провайдер / Когда: При наплыве игроков, Сразу после входа или техработ, Изредка, случайно - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка сервера, Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: на случай заполнения таблицы сдерживать наплыв подключений очередью на вход, переиспользовать соединения вместо повторяющихся коротких (включая вызовы между серверами), первым закрывать соединения без хартбитов. Клиент: если подключение не удалось или оборвалось, увеличивать интервал между попытками и добавлять случайный разброс (чтобы при заполненной таблице игроки не ломились обратно все разом). - Команда инфраструктуры, задачи: Сеть: увеличить таблицу отслеживания соединений на файрволе, исключить игровые порты из отслеживания соединений, настроить маршрутизацию так, чтобы трафик в обе стороны шёл через один и тот же файрвол, проверить настройки проверки окна TCP на файрволе. Серверы и ОС: увеличить таблицу в Linux (nf_conntrack_max), исключить игровые порты из отслеживания соединений (NOTRACK), проверить настройку проверки окна TCP (nf_conntrack_tcp_be_liberal), в AWS проверить ещё и conntrack_allowance_exceeded. - Цифры для ориентира: Лимит conntrack в Linux по умолчанию (nf_conntrack_max) в зависимости от объёма памяти составляет от десятков до сотен тысяч записей. Когда текущее число (nf_conntrack_count) доходит до лимита, в логе появляется «nf_conntrack: table full, dropping packet». - На графике: Упор в лимит (плато) (число записей conntrack (nf_conntrack_count), число неудачных новых подключений) - Где смотреть: На серверах Linux посмотреть nf_conntrack_count и nf_conntrack_max, «nf_conntrack: table full, dropping packet» в dmesg, drop и invalid в /proc/net/stat/nf_conntrack (по строке на ядро, шестнадцатеричные значения). На файрволе посмотреть заполнение таблицы сессий и логи дропов, в AWS conntrack_allowance_exceeded в ethtool -S - Подтверждает: Число записей выходит на плато у лимита, и одновременно растут логи table full и drop или conntrack_allowance_exceeded. При асимметричном маршруте до лимита далеко, но invalid и логи дропов файрвола растут на соединениях определённого маршрута - Опровергает: Если до лимита далеко, а invalid и логи дропов не меняются, причина другая. Если в таблице есть место, но CPU или пакеты в секунду на файрволе упираются в потолок, это «Перегрузка промежуточного оборудования (файрвол, IPS, защита от DDoS)» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 помечает как INVALID только RST вне окна - [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 · CT --notrack в таблице raw исключает трафик из отслеживания соединений - [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: по строке на ядро, шестнадцатеричные значения, столбцы entries, invalid, insert_failed, drop, early_drop и др. #### rt-appliance-pps · Перегрузка промежуточного оборудования (файрвол, IPS, защита от DDoS) · Inline appliance PPS / CPU overload Файрволы, системы предотвращения вторжений (IPS) и оборудование защиты от DDoS проверяют каждый проходящий пакет. Как только поток превышает их возможности, необработанные пакеты выбрасываются. - Почему → Следствие → На экране: В часы пик и на событиях идёт больше сотен тысяч мелких игровых пакетов в секунду, или правила проверки слишком тяжёлые → Упор в лимит CPU или пакетов в секунду, оборудование отбрасывает пакеты. При ложном срабатывании блокируются и нормальные пакеты → Фризы и телепортация одновременно на всех серверах за этим оборудованием, хуже всего при наплыве игроков - Симптомы: Фриз, Перемотка, Телепортация, Дисконнект / Факторы: Потери, Задержка - У кого: Весь сервер, Один регион или провайдер / Когда: Вечерний пик, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: Передать команде инфраструктуры профиль игрового трафика (порты, размер пакетов, пакеты в секунду), собирать мелкие сообщения одного тика и отправлять разом, чтобы уменьшить число пакетов. - Команда инфраструктуры, задачи: Смотреть CPU, пакеты в секунду и счётчики дропов оборудования вместе с игровыми метриками, закладывать производительность оборудования из расчёта на мелкие пакеты, исключить игровые порты из тяжёлых проверок, подогнать правила защиты от DDoS под профиль игрового трафика. - Цифры для ориентира: Цифра «10 Gbps» в характеристиках оборудования часто указана для больших пакетов по 1 500 байт. Игровых пакетов размером около 100 байт при той же пропускной способности в 10 с лишним раз больше, поэтому лимит пакетов в секунду исчерпывается раньше, даже если линия связи выглядит свободной. - На графике: Упор в лимит (плато) (пакеты в секунду и загрузка CPU оборудования, число дропов на оборудовании) - Где смотреть: Посмотреть счётчики CPU, пакетов в секунду и дропов на оборудовании и с одинаковым интервалом сравнить число пакетов на портах коммутаторов до и после оборудования. Наложить на тот же экран онлайн и долю повторных передач на серверах - Подтверждает: В часы пик и на событиях пакеты в секунду или 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 каждый раз удваивается → Обычно всё нормально, но в моменты передачи больших данных (инвентарь, места скопления игроков, загрузка при входе) встают и все следующие мелкие пакеты: фриз, в итоге дисконнект или бесконечная загрузка - Симптомы: Фриз, Дисконнект, Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Один регион или провайдер, Только у меня / Когда: При определённом действии, Сразу после входа или техработ - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка сервера - Команда разработки, задачи: Чтобы снизить размер на стороне сервера, задать в сокете максимальный размер сегмента (TCP_MAXSEG). Одного дробления сообщений в игровом коде недостаточно (TCP заново собирает отправляемые данные в куски размером MSS). - Команда инфраструктуры, задачи: Сеть: ограничить MSS на пограничном оборудовании (MSS clamping), разрешить ICMP о превышении размера (тип 3, код 4, fragmentation needed) на файрволах и в сетевых ACL облака. Серверы и ОС: настроить 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 и ping на 1 500 байт с включённым DF (ping -M do -s 1472) и сравнить - Подтверждает: Пакеты, заполненные до MSS, с одним и тем же номером последовательности повторно передаются снова и снова с удваивающимся интервалом, а пакеты поменьше проходят. ICMP о превышении размера (фильтр Wireshark icmp.type == 3 and icmp.code == 4) не приходят, на маленький ping ответ есть, а большой ping с DF пропадает без ответа - Опровергает: Если вместе с большими пропадают и мелкие пакеты, это потери, не зависящие от размера («Переполнение очереди в узком месте (потери от перегрузки)», «Смена маршрута и неисправный путь ECMP»). Если ICMP о превышении размера приходят и pmtu в ss -ti уменьшается, поиск MTU пути работает правильно - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: С tcp_mtu_probing=1 чёрная дыра распознаётся только после нескольких секунд таймаутов повторной передачи (соответствует tcp_retries1=3), и лишь тогда MSS снижается до 1 024 байт. Всё это время фриз, поэтому tcp_mtu_probing оставляют последней страховкой. В первую очередь нужно ограничение 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 · Проблема чёрной дыры PMTU: ICMP заблокирован, и раз за разом пропадают только большие пакеты - [RFC 4821: Packetization Layer Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc4821) · IETF · Способ, при котором транспортный уровень подбирает размер пакета без ICMP (основа tcp_mtu_probing в Linux) - [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 удваивается - [iptables-extensions(8) — Linux manual page](https://man7.org/linux/man-pages/man8/iptables-extensions.8.html) · netfilter · TCPMSS --clamp-mss-to-pmtu: обход проблемы, когда большие пакеты застревают на участке, блокирующем ICMP, через ограничение MSS в SYN - [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 · MTU интернет-шлюза и VPN 1 500, для PMTUD нужен ICMP тип 3, код 4, и если его блокируют группа безопасности или сетевой ACL, он не доходит - [MTU considerations | Cloud VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/mtu-considerations) · Google Cloud · MTU шлюза Cloud VPN 1 460 байт, MTU полезной нагрузки туннеля IPv4 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 · Выводит по строке на каждую повторную передачу, -s добавляет номер последовательности повторно переданного пакета - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · mss, pmtu (MTU пути) и backoff (сколько раз удвоился RTO) в ss -i - [ping(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ping.8.html) · iputils · -M do включает DF и не отправляет пакеты больше известного ядру MTU пути, -s задаёт размер данных (8 байт заголовка ICMP сверх него) - [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 провайдера, файрвол, балансировщик нагрузки или облачная группа безопасности удаляют неактивную запись → Как только игрок снова начинает двигаться, идут повторные передачи и затем дисконнект, или дисконнект сразу - Симптомы: Дисконнект, Фриз / Факторы: Потери - У кого: Только у меня, Один регион или провайдер / Когда: После бездействия - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера, Команда инфраструктуры · Сетевая инфраструктура, Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: Клиент: слать хартбиты с интервалом не больше половины самого короткого таймаута простоя (запись в роутере игрока и в CGNAT провайдера надёжно обновляют только пакеты, исходящие изнутри, а таймауты мы изменить не можем, поэтому слать должен клиент), при обрыве автоматически переподключаться. Сервер: отвечать на хартбиты и, если их нет определённое время, первым закрывать соединение (сократить интервал TCP keepalive опциями сокета вроде TCP_KEEPIDLE, быстрее замечать обрыв через TCP_USER_TIMEOUT), продолжать сессию по токену сессии. - Команда инфраструктуры, задачи: Сеть: собрать таймауты простоя файрволов и балансировщиков на маршруте и передать команде разработки, на наших файрволах и балансировщиках при необходимости увеличить. Серверы и ОС: проверить время отслеживания соединений в облачной группе безопасности и передать команде разработки. - Цифры для ориентира: Сколько держится запись TCP, зависит от устройства: от нескольких минут до нескольких часов. Если облачная группа безопасности отслеживает соединения, у типов инстансов AWS Nitro v6 запись удаляется по умолчанию через 350 с (у остальных типов через 5 дней, см. причину «Истечение отслеживания соединений в облачной группе безопасности»). TCP keepalive в Linux по умолчанию срабатывает так: «проверить после 2 часов простоя», а это позже, чем у большинства устройств. - На графике: Массовый обрыв соединений (число обрывов, время бездействия перед обрывом) - Где смотреть: Посмотреть последние минуты оборвавшихся соединений в захвате пакетов на стороне сервера, у живых соединений смотреть время простоя по lastsnd и lastrcv в ss -ti (сколько ms прошло с последней отправки и последнего приёма). Заодно смотреть TcpExtTCPAbortOnTimeout в nstat (сколько соединений брошено по истечении таймера) - Подтверждает: У каждого оборвавшегося соединения время простоя перед обрывом превысило примерно одно и то же значение (таймаут простоя устройства на маршруте, например 350 с в группе безопасности инстансов AWS Nitro v6), и с первого пакета после простоя идут одни повторные передачи без ACK, пока отправитель не прекратит попытки, или сразу возвращается RST - Опровергает: Если обрывы бывают и во время игры независимо от времени простоя, причина другая («Смена маршрута и неисправный путь ECMP», «Отбрасывание пакетов файрволом или conntrack»). Если хартбиты по соединению ходят с интервалом не больше половины самого короткого таймаута простоя, эта причина исключается - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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-соединений по умолчанию: 350 с у типов инстансов Nitro v6, 432 000 с (5 дней) у остальных. Рекомендация слать keepalive с интервалом меньше 5 минут - [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 · lastsnd и lastrcv в ss -i: время с последней отправки и последнего приёма (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) → Временные потери во время переключения маршрута или постоянные потери только у соединений на этом пути → Внезапный фриз на несколько секунд, потом перемотка, или «после перезахода лучше» (соединение попало на другой путь) - Симптомы: Фриз, Перемотка, Телепортация / Факторы: Потери - У кого: Один регион или провайдер / Когда: Изредка, случайно - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда разработки · Разработка сервера, Внешние стороны · Внешние стороны - Команда разработки, задачи: Записывать статистику повторных передач по соединениям (TCP_INFO), чтобы можно было найти IP, порт и время у пострадавших игроков, не рвать сразу соединение, вставшее на несколько секунд. - Команда инфраструктуры, задачи: Отслеживать долю повторных передач по регионам и провайдерам, проверять, меняется ли путь при перезаходе, иметь линии нескольких провайдеров, проверить пути ECMP и LAG на нашем оборудовании на неисправные линки, измерять маршрут через тот же TCP-порт, что и игра (mtr --tcp --port. Путь выбирается по адресу и порту, поэтому обычный ping может пойти другим путём и показать, что всё в порядке). - Внешние стороны, задачи: Сообщить провайдеру о неисправном пути, приложив результаты измерения маршрута через тот же TCP-порт и сравнение до и после перезахода. - На графике: Ступенька вверх с определённого момента (RTT (пинг), доля повторных передач по регионам и провайдерам) - Где смотреть: Собрать повторные передачи по соединениям через bcc tcpretrans -c и выделить адреса и порты пострадавших игроков, затем запустить mtr через тот же TCP-порт, что и игра (mtr -T -P PORT), с сервера к игроку и от игрока к серверу и сравнить. Сравнить и результаты до и после перезахода - Подтверждает: С определённого момента RTT региона или провайдера меняется ступенькой и на несколько секунд сгущаются потери, или даже внутри одного провайдера постоянно повторно передают только некоторые соединения (сочетания адреса и порта), а перезаход помогает. Бывает, что обычный ping в порядке, а потери видны только в TCP mtr - Опровергает: Если в вечерний пик ухудшаются все соединения этого провайдера, это «Переполнение очереди в узком месте (потери от перегрузки)». Если плохо только у одного игрока и потери начинаются уже в 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) использует TCP SYN вместо ICMP, -P (--port) задаёт порт назначения - [Demonstrations of tcpretrans, the Linux eBPF/bcc version](https://raw.githubusercontent.com/iovisor/bcc/master/tools/tcpretrans_example.txt) · IO Visor · Выводит по строке с адресом и портом другой стороны на каждую повторную передачу, -c подсчитывает повторные передачи по потокам #### rt-spurious-delay · Ложные повторные передачи из-за скачков задержки · Spurious RTO from delay spikes Пакет цел и лишь ненадолго сильно задерживается. Если эта задержка дольше RTO, отправитель считает пакет потерянным и передаёт его повторно. - Почему → Следствие → На экране: Bufferbloat, энергосбережение Wi-Fi, переключение состояний радиомодуля в мобильной сети или пауза виртуальной машины дают мгновенную задержку в сотни ms → RTO истекает раньше, происходит повторная передача, вскоре приходит и оригинал (получатель получает дубликат) → Фриз и перемотку вызывает сам скачок задержки. Ложная повторная передача почти не удлиняет фриз, она только поднимает метрики повторных передач, и её принимают за потери - Симптомы: Фриз, Перемотка, Задержка ввода / Факторы: Задержка, Джиттер - У кого: Только у меня, Весь сервер / Когда: Изредка, случайно, После бездействия - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка клиента - Команда разработки, задачи: На клиентах с Android 10 и новее запрашивать во время игры режим Wi-Fi с низкой задержкой (блокировка Wi-Fi WIFI_MODE_FULL_LOW_LATENCY, действует, только когда экран включён и игра на переднем плане), чтобы уменьшить скачки задержки из-за энергосбережения. - Команда инфраструктуры, задачи: Избегать инстансов с burst-производительностью, не занижать сильно минимальный RTO, оставить включёнными F-RTO и временные метки (tcp_frto, tcp_timestamps), смотреть метрики повторных передач вместе с TCPSpuriousRTOs и TCPDSACKRecv из nstat, чтобы не принять их за потери. - Внешние стороны, задачи: Чтобы уменьшить сами скачки задержки, посоветовать игрокам SQM на роутере и отключение энергосбережения Wi-Fi. - Цифры для ориентира: Linux с помощью F-RTO обнаруживает ложные RTO и может отменить снижение скорости отправки. Проверяют по TCPSpuriousRTOs в nstat (сколько RTO признано ложными) и TCPDSACKRecv (сколько раз получатель сообщил «это уже получено»). - На графике: Случайные всплески (RTT (пинг), число ложных RTO) - Где смотреть: Запуская nstat раз в минуту, смотреть вместе прирост 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: по ACK, пришедшим после RTO, определяет, был ли 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 (ложные RTO, обнаруженные F-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): блокировка Wi-Fi с низкой задержкой, действует, только когда устройство подключено к AP, экран включён и приложение на переднем плане - [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 (агрегирование линков) с распределением по пакетам и моменты смены маршрута → Следующие пакеты приходят раньше, накапливаются 3 дублирующих ACK → быстрая повторная передача → На редкие игровые пакеты почти не влияет. Медленнее идут крупные обновления в местах скопления игроков и загрузка патчей, иногда микрофризы - Симптомы: Микрофризы, Задержка ввода / Факторы: Джиттер - У кого: Один регион или провайдер, Весь сервер / Когда: Всегда, При наплыве игроков - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Сеть: балансировка по соединениям вместо балансировки по пакетам (ECMP и LAG по хешу адресов и портов). Серверы и ОС: использовать RACK (обнаружение потерь по времени, устойчиво к нарушению порядка. Обнаружив ложную повторную передачу по DSACK, автоматически расширяет допуск на нарушение порядка), проверить степень нарушения порядка, которую Linux автоматически оценивает для каждого соединения (значение reordering в ss -ti, начальное значение tcp_reordering=3). - На графике: Высоко с самого начала (число обнаруженных нарушений порядка, число полученных DSACK) - Где смотреть: Посмотреть в nstat TcpExtTCPSACKReorder и TcpExtTCPTSReorder (сколько раз обнаружено нарушение порядка) и TcpExtTCPDSACKRecv, по соединениям смотреть reordering (выводится, если не равно 3) и reord_seen в ss -ti. В захвате пакетов использовать фильтр Wireshark tcp.analysis.out_of_order - Подтверждает: Счётчики нарушения порядка и DSACK стабильно растут независимо от времени суток, а у соединений через определённый путь или оборудование значение reordering больше 3. В захвате на стороне получателя следующий пакет приходит первым, а предыдущий тоже вскоре доходит - Опровергает: Если счётчики нарушения порядка не меняются, а растёт TcpExtTCPLostRetransmit, это реальные потери. Если DSACK растёт только в моменты скачков RTT, это «Ложные повторные передачи из-за скачков задержки» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Источники: - [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 · Быстрая повторная передача по третьему дублирующему 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), настройка RACK в tcp_recovery - [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:значение, если reordering соединения отличается от значения по умолчанию 3, и 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 · tcpi_reord_seen в tcp_info: сколько раз соединение сталкивалось с нарушением порядка - [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 или выбрасываются при её переполнении → Игровые пакеты от сервера в основном приходят вовремя. Ввод игрока стоит в той же очереди отдачи и запаздывает: задержка ввода и откидывание назад, иногда ложные повторные передачи - Симптомы: Задержка ввода, Откидывание назад / Факторы: Задержка, Потери - У кого: Все в одном доме / Когда: Изредка, случайно, Вечерний пик - Основной ответственный: Внешние стороны · Внешние стороны / Совместно: Команда разработки · Разработка клиента - Команда разработки, задачи: При скачке пинга показывать на экране состояние сети и подсказку «Проверьте, какие программы сейчас что-то выгружают». - Внешние стороны, задачи: Посоветовать игрокам SQM на роутере, чтобы очередь отдачи была короткой, приоритет для мелких пакетов (ACK) и ограничение скорости отдачи (выгрузка видео, облачный бэкап). - Цифры для ориентира: Каждый следующий ACK подтверждает и предыдущие, поэтому потеря нескольких ACK обычно не страшна. Проблема в задержке ACK в очереди. - На графике: Высоко только у некоторых (RTT (пинг) по соединениям) - Где смотреть: С ПК игрока сравнить ping до игрового сервера при включённой и выключенной выгрузке (видео, облачный бэкап). На сервере посмотреть rtt соединения этого игрока в ss -ti - Подтверждает: Только во время выгрузки ping поднимается до сотен ms и появляются задержка ввода и откидывание назад, а после остановки выгрузки всё быстро возвращается. На сервере в это время растёт и rtt этого соединения - Опровергает: Если потери и задержка появляются независимо от выгрузки, это «Потери на беспроводном участке» или причина на маршруте. Если запаздывает только направление от сервера к игроку и выгрузка ни при чём, это «Переполнение очереди в узком месте (потери от перегрузки)» - Чем проверить: Проверка на стороне игрока - Источники: - [RFC 3449: TCP Performance Implications of Network Path Asymmetry](https://www.rfc-editor.org/rfc/rfc3449) · IETF · На асимметричных линиях с узкой отдачей задержка или потеря ACK снижает производительность TCP; 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 · rtt (среднее время пути туда и обратно) и rttvar (разброс) в ss -i #### rt-rto-setting · Неподходящая настройка RTO · RTO min too low or too high Если слишком занизить минимальный RTO, от малейшей задержки возникают ложные повторные передачи, а значение по умолчанию (200 ms) для игры слишком велико, и каждая потеря даёт долгий фриз. - Почему → Следствие → На экране: Минимальный RTO сильно занижен под ЦОД, или на интернет-участке оставлено значение по умолчанию → Если мало, лавина повторных передач даже от мгновенной задержки, если много, долгое ожидание при каждой потере → При значении по умолчанию каждая потеря даёт фриз на сотни ms, потом перемотку. Если слишком занизить, фризы короче, но резко растут ложные повторные передачи и линия связи загружается зря - Симптомы: Фриз, Перемотка, Задержка ввода / Факторы: Задержка - У кого: Весь сервер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Серверная инфраструктура / Совместно: Команда разработки · Разработка сервера - Команда разработки, задачи: В Linux 6.15 и новее рассмотреть снижение потолка RTO для игровых соединений через TCP_RTO_MAX_MS (время до отказа от соединения тоже сокращается, поэтому вместе с этим задать время признания обрыва через TCP_USER_TIMEOUT), снижать минимальный RTO опцией сокета TCP_RTO_MIN_US (6.15 и новее) только для внутренних соединений между серверами, рассмотреть опцию сокета TCP_THIN_LINEAR_TIMEOUTS, чтобы только у игровых соединений RTO не удваивался при серии таймаутов. - Команда инфраструктуры, задачи: Снижать rto_min по маршрутам только для внутренних соединений между серверами, на интернет-участке оставить значение по умолчанию и компенсировать настройками RACK-TLP и thin stream (tcp_thin_linear_timeouts). - Цифры для ориентира: В Linux RTO = время пути туда и обратно + max(200 ms, разброс RTT×4). После каждой неудачи удваивается, максимум 120 с. В Linux 6.15 и новее этот потолок можно снизить до 1 с через TCP_RTO_MAX_MS. - На графике: Высоко с самого начала (RTO по соединениям, число ложных RTO) - Где смотреть: Посмотреть настройку минимального RTO на сервере (rto_min в ip route show, в Linux 6.11 и новее sysctl net.ipv4.tcp_rto_min_us), rto и rtt в ss -ti и прирост TcpExtTCPSpuriousRTOs в nstat - Подтверждает: На сервере с заниженным минимумом rto интернет-соединений вплотную прижат к rtt, и TcpExtTCPSpuriousRTOs сильно растёт. При значении по умолчанию rto игровых соединений больше rtt на 200 ms и более, и каждая потеря даёт фриз на это время - Опровергает: Если rto соответствует расчёту по умолчанию (около rtt + 200 ms) и ложных 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 с, после каждой неудачи удвоение, максимум, если его вводят, не меньше 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 200 ms, 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 (по умолчанию 200 ms) - [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 · Добавлена опция сокета TCP_RTO_MIN_US, задающая минимальный RTO для каждого сокета, начиная с 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 · rto (ms) и rtt в ss -i - [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html) · Linux kernel · TcpExtTCPSpuriousRTOs: ложные RTO, обнаруженные F-RTO #### rt-thin · Медленное восстановление потерь в thin stream · Thin streams fall back to RTO Когда мелкие пакеты отправляются редко, как в играх, RTO наступает раньше, чем соберутся «3 следующих пакета». От тех же потерь поток стоит намного дольше, чем при больших передачах. - Почему → Следствие → На экране: Интервал между пакетами около 100 ms, поэтому неподтверждённых пакетов (in-flight) всего несколько → Чтобы собрались 3 дублирующих ACK, нужно больше 300 ms, поэтому раньше срабатывает RTO (пинг + 200 ms), при серии потерь он удваивается → От одной потери фриз около 0,3 с, если потеряна и повторная передача, фриз почти на 1 с, потом перемотка - Симптомы: Фриз, Перемотка / Факторы: Потери, Остановка - У кого: Только у меня, Весь сервер / Когда: Изредка, случайно - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: включить TCP_NODELAY (при включённом Nagle у RACK нет следующих пакетов, по которым он принимает решение), пакеты реального времени отправлять по UDP со своей схемой повторных передач. Клиент: включить TCP_NODELAY, пакеты реального времени отправлять так же, как сервер (UDP). - Команда инфраструктуры, задачи: Использовать RACK-TLP (по умолчанию в современных Linux), через tcp_thin_linear_timeouts не давать RTO удваиваться при серии таймаутов. - Цифры для ориентира: При интервале пакетов 100 ms и пинге 60 ms до быстрой повторной передачи около 360 ms (пока не придут 3 следующих пакета и не вернётся их подтверждение), а RTO около 260 ms. С RACK пакет отправляется повторно, как только возвращается подтверждение следующего пакета, примерно через 160 ms. Если интервал пакетов больше 200 ms, RACK тоже не быстрее RTO. - На графике: Провал, затем пачка (объём приёма по соединениям, число истечений RTO) - Где смотреть: Сравнить прирост TcpExtTCPTimeouts (истечения RTO), TcpExtTCPFastRetrans (быстрые повторные передачи), TcpExtTCPLossProbes и TcpExtTCPLossProbeRecovery (TLP) в nstat и посмотреть rto и backoff игровых соединений в ss -ti. Также проверить значения net.ipv4.tcp_recovery, tcp_early_retrans и tcp_sack на сервере - Подтверждает: Среди повторных передач истечений RTO больше, чем быстрых повторных передач, и у игровых соединений часто backoff больше 0 (соединение переживает RTO). Во время фриза принятый объём равен 0, а после восстановления всё приходит разом - Опровергает: Если большие передачи на том же сервере стоят так же долго, проблема в самих потерях и от формы соединения не зависит. Если проблема сосредоточена на соединениях без SACK и временных меток, это «Удаление опций TCP промежуточным оборудованием» - Чем проверить: Инструменты инфраструктуры (игровой код не нужен) - Подробнее: Раньше в Linux была и опция повторной передачи по одному дублирующему ACK для thin stream (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 (редкие потоки вроде игровых) быстрая повторная передача работает плохо, и восстановление зависит от длинного таймаута; критерий: меньше 4 неподтверждённых пакетов (in-flight) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · Быстрая повторная передача по третьему дублирующему ACK - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACK определяет потерю по тому, что дошёл пакет, отправленный позже; ожидание TLP равно 2·SRTT (если неподтверждённый пакет один, добавляется запас на отложенный 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 200 ms, критерий thin stream (меньше 4 пакетов in-flight) и 6 линейных повторов - [tcp: remove thin_dupack feature](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4a7f6009441144783e5925551c72e3f2e1b0839b) · Linux kernel · В январе 2017 года 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 не удваивается (по умолчанию выключено) - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP_NODELAY отключает алгоритм Нейгла - [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 · rto (ms) и backoff (сколько раз удвоился RTO) в ss -i #### rt-sack-stripped · Удаление опций TCP промежуточным оборудованием · Middlebox strips TCP options Если некоторые файрволы или ускорители удаляют или меняют опции TCP, то при потере нескольких пакетов они восстанавливаются по одному за RTT или окно (сколько можно отправить за раз) становится маленьким, и передача замедляется. - Почему → Следствие → На экране: «Нормализация TCP» на файрволе или старый ускоритель удаляют опции SACK, временных меток и масштабирования окна → Если потеряно несколько пакетов, они восстанавливаются по одному за RTT, окно ограничено 64 KB → Каждая потеря даёт намного более долгий фриз (без SACK нельзя использовать и RACK-TLP), после которого перемотка. Большие передачи вроде патчей тоже идут медленно - Симптомы: Фриз, Перемотка / Факторы: Остановка, Задержка - У кого: Один регион или провайдер, Весь сервер / Когда: Всегда - Основной ответственный: Команда инфраструктуры · Сетевая инфраструктура / Совместно: Команда инфраструктуры · Серверная инфраструктура - Команда инфраструктуры, задачи: Сеть: отключить нормализацию TCP на этом оборудовании, проверить и рандомизацию номеров последовательности на файрволе, сравнить опции SYN по захватам пакетов на обоих концах. Серверы и ОС: проверить в ss -ti, не сосредоточены ли соединения без sack и wscale на определённом маршруте (ПК с Windows в зависимости от настроек не используют ts, поэтому отсутствие только ts может быть нормой), проверить, что net.ipv4.tcp_sack на сервере равен 1. - На графике: Высоко с самого начала (число восстановлений, начатых без SACK (TcpExtTCPRenoRecovery)) - Где смотреть: Проверить в ss -ti, есть ли у каждого соединения отметки sack и wscale, и посмотреть в nstat соотношение TcpExtTCPRenoRecovery (восстановления без SACK) и TcpExtTCPSackRecovery, а также TcpExtTCPSACKDiscard (число SACK-блоков, выброшенных как несогласованные). На подозрительном маршруте захватить SYN на обоих концах и сравнить опции (tcp.options.sack_perm в Wireshark и др.) - Подтверждает: sack и wscale отсутствуют только у соединений через определённый маршрут или оборудование, и доля TcpExtTCPRenoRecovery высокая. Опция разрешения SACK, которая была в SYN на стороне отправителя, отсутствует в 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, за один RTT можно узнать только об одном потерянном пакете - [RFC 7323: TCP Extensions for High Performance](https://www.rfc-editor.org/rfc/rfc7323) · IETF · Без опции масштабирования окна окно не больше 2^16 = 64 KiB - [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 · TLP в Linux планируется только для соединений с 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 · Коммит с исправлением уязвимости обработки SACK 2019 года (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 (опция разрешения SACK в SYN) #### rt-zero-window · Нулевое окно (остановка, похожая на повторную передачу) · Zero window, often mistaken for retransmission Если программа-получатель не успевает вовремя читать сокет и буфер заполняется, отправитель останавливает передачу и шлёт только пробы нулевого окна. С линией связи это не связано. - Почему → Следствие → На экране: Кадр на клиенте завис или поток сервера заблокирован, и сокет не читается → Окно приёма становится 0, отправитель останавливает передачу и шлёт только пробы (интервал постепенно растёт) → Фриз, потом перемотка. В захвате пакетов видно «ZeroWindow», потерь нет - Симптомы: Фриз, Перемотка / Факторы: Остановка - У кого: Только у меня, Весь сервер / Когда: При наплыве игроков, Изредка, случайно - Основной ответственный: Команда разработки · Разработка клиента / Совместно: Команда разработки · Разработка сервера, Команда инфраструктуры · Серверная инфраструктура - Команда разработки, задачи: В захвате пакетов сначала найти сторону, отправившую ZeroWindow (ту, что не успевает читать сокет), читать сетевые данные непрерывно в отдельном потоке, задать подходящий размер буфера приёма. Клиент: устранить причины зависания кадров вроде загрузки и GC. Сервер: устранить причины блокировки потока, читающего сокет. - Команда инфраструктуры, задачи: Добавить в мониторинг TcpExtTCPToZeroWindowAdv из nstat на сервере (сколько раз сервер объявил окно приёма равным 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 после ненулевого значения - [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 растёт с каждой пробой (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 · Recv-Q в ss для соединения: число байт, которые получены, но ещё не прочитаны программой #### rt-syn · Повторная передача запроса на подключение (SYN) · SYN retransmission on connect Если запрос на подключение теряется из-за переполнения очереди подключений (backlog) или блокировки файрволом, ОС клиента отправляет его повторно через 1 с, а затем с растущими интервалами. - Почему → Следствие → На экране: Сразу после техработ из-за наплыва подключений переполняется очередь подключений сервера, или файрвол либо защита от DDoS выбрасывают SYN → ОС клиента повторно отправляет SYN через 1 с, затем через заданные интервалы (в старых Linux 1 с → 2 с → 4 с) → После нажатия кнопки входа задержка ровно в целые секунды (1 с, 3 с), при постоянных неудачах ошибка входа или бесконечная загрузка - Симптомы: Ошибка входа / бесконечная загрузка / Факторы: Потери - У кого: Весь сервер, Один регион или провайдер / Когда: Сразу после входа или техработ - Основной ответственный: Команда разработки · Разработка сервера / Совместно: Команда инфраструктуры · Серверная инфраструктура, Команда инфраструктуры · Сетевая инфраструктура, Команда разработки · Разработка клиента - Команда разработки, задачи: Сервер: увеличить аргумент backlog в listen (вместе с somaxconn), вовремя вызывать accept в игровом сервере, очередь на вход. Клиент: увеличивать интервал повторных попыток подключения (со случайным разбросом). - Команда инфраструктуры, задачи: Серверы и ОС: переполнение очереди подключений на сервере проверять по TcpExtListenOverflows и TcpExtListenDrops в nstat и по предупреждению «Possible SYN flooding» в логе, увеличить somaxconn (вместе с аргументом listen), включить SYN cookies. Сеть: ослабить ограничения SYN на файрволе и в защите от DDoS. - Цифры для ориентира: В Linux (включая Android) первая повторная передача SYN идёт через 1 с. Старые ядра дальше каждый раз удваивают интервал и отправляют повторно на 1, 3, 7, 15 с …, а ядра 6.5 и новее отправляют SYN повторно пять раз, в моменты 1, 2, 3, 4, 5 с, и только потом удваивают интервал (7, 11, 19 с …) (tcp_syn_linear_timeouts=4). На телефонах с Android после обновления ОС часто остаётся ядро, с которым устройство вышло, поэтому даже на одной версии Android поведение может отличаться от устройства к устройству. В любом случае, если все попытки неудачны, примерно через 2 минуты попытки прекращаются. В Windows в зависимости от версии и настроек интервалы растут начиная с 1 с или 3 с, а повторов 2–4, так что отказ наступает через 20–30 с (значение для конкретного ПК видно в Max SYN Retransmissions в выводе netsh int tcp show global). - На графике: Всплеск сразу после входа или техработ (число попыток подключения, число переполнений очереди подключений) - Где смотреть: На сервере посмотреть TcpExtListenOverflows и TcpExtListenDrops в nstat и предупреждение «Possible SYN flooding on port» в dmesg, через 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 с, при каждой повторной передаче удваивается - [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 по умолчанию: 2 повторные передачи SYN, первое ожидание 3 с с удвоением, после последней ожидание ещё вдвое дольше и отказ (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 зависит от ОС, смотрят его через Max SYN Retransmissions в выводе netsh int tcp show global - [listen(2) — Linux manual page](https://man7.org/linux/man-pages/man2/listen.2.html) · Linux man-pages · Аргумент backlog в listen обрезается до 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 · Для слушающего сокета Recv-Q в ss означает число соединений, ждущих accept, а Send-Q лимит backlog ## Источники по главам ### #l-client-game - [Slow rendering](https://developer.android.com/topic/performance/vitals/render) · Android (Google) · Чтобы держать 60 FPS, кадр нужно отрисовать за 16 ms, а если не успеть, кадр пропускается, и это выглядит как микрофризы - [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, главный поток стоит, пока проверяется вся куча, и кадр не укладывается в 16 ms - [Scripting.GarbageCollector.CollectIncremental](https://docs.unity3d.com/ScriptReference/Scripting.GarbageCollector.CollectIncremental.html) · Unity · Квант инкрементального GC 3 ms в эксперименте: значение incrementalTimeSliceNanoseconds по умолчанию 3 ms - [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 в эксперименте: если нового кадра нет, экран 60 Hz ещё раз показывает предыдущий - [Set fixed timestep to optimize physics simulation frequency](https://docs.unity3d.com/Manual/physics-optimization-cpu-frequency.html) · Unity · Фиксированный шаг с нагоном в эксперименте: если кадр длиннее интервала шага, за один кадр шаг выполняется несколько раз, и нагрузка растёт - [Handling variation in time](https://docs.unity3d.com/Manual/time-handling-variations.html) · Unity · Лимит нагона в эксперименте (до 5 шагов за кадр): Unity ограничивает игровое время одного кадра максимум 1/3 с, чтобы не допустить спирали нагона, и игровые часы отстают на величину избытка ### #l-client-os - [Multitasking](https://learn.microsoft.com/en-us/windows/win32/procthread/multitasking) · Microsoft · Вытесняющая многозадачность: каждый поток получает квант времени (около 20 ms, зависит от ОС и CPU), а когда он исчерпан, процессор переходит к следующему потоку - [Scheduling Priorities](https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities) · Microsoft · Среди готовых к выполнению потоков кванты времени по очереди (round robin) получают потоки с наивысшим приоритетом - [Priority Boosts](https://learn.microsoft.com/en-us/windows/win32/procthread/priority-boosts) · Microsoft · Процессу окна на переднем плане (foreground) приоритет поднимается до уровня фоновых процессов или выше - [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) · В режиме Wi-Fi с низкой задержкой на Android 10 и новее фреймворк явно отключает энергосбережение 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,6 ms в эксперименте: стандартный интервал тика системных часов Windows 15,6 ms - [timeBeginPeriod function (timeapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod) · Microsoft · Таймер 1 ms в эксперименте: программа может запросить повышенное разрешение таймера через 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,5 ns, L2 7 ns, основная память 100 ns, путь туда и обратно внутри одного ЦОД 0,5 ms, позиционирование диска 10 ms (данные 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 · Задержка 99,99% (four-nines latency) серверного NVMe SSD 130 µs: основание для того, что чтение с SSD и повторное чтение из свопа в таблице около 100 µs - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_us по умолчанию 200 000 µs: минимальное ожидание повторной передачи TCP в Linux 200 ms (строка о повторной передаче 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 1 ms и меньше (в тексте полный GC кучи от сотен ms до нескольких секунд, в таблице GC большой кучи 1 с) - [Available Collectors](https://docs.oracle.com/en/java/javase/25/gctuning/available-collectors.html) · Oracle · ZGC немного жертвует пропускной способностью ради максимальной паузы меньше 1 ms, пауза не зависит от размера кучи - [Garbage Collector Implementation](https://docs.oracle.com/en/java/javase/25/gctuning/garbage-collector-implementation.html) · Oracle · Поколенческая сборка: minor-сборка только Young короткая, а major-сборка всей кучи длится гораздо дольше (поколенческий режим в эксперименте с GC) - [The Z Garbage Collector](https://docs.oracle.com/en/java/javase/25/gctuning/z-garbage-collector1.html) · Oracle · ZGC выполняет дорогую работу конкурентно и не останавливается дольше 1 ms, но если сборка не успевает освобождать память, приложение может встать в ожидании GC (конкурентный режим в эксперименте с GC) - [A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide) · Go · Фаза разметки GC занимает 25% CPU, и программа на это время замедляется, а при большом числе аллокаций горутины тратят время на помощь GC (assist) и задерживаются (конкурентный режим в эксперименте с GC) - [Go 1.8 Release Notes](https://go.dev/doc/go1.8) · Go · Паузы GC в Go обычно меньше 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 · HDD 7 200 rpm: случайное чтение 4K 170 IOPS, средняя задержка вращения 4,16 ms (в тексте у HDD чуть больше 150 операций в секунду, HDD в эксперименте с диском) - [D3-S4520 SSD](https://www.solidigm.com/products/data-center/d3/s4520.html) · Solidigm · SATA SSD: случайное чтение и запись 4 KB до 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 только 30 минут раз в 24 часа, затем возвращаются к базовой (например, у t4g.2xlarge базовая 4 000 и максимальная 15 700 IOPS, допущение об облачном burst-инстансе в эксперименте с диском) - [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, пишущий процесс сам берёт на себя запись на диск (лимит записи ОС в эксперименте с диском) - [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 · Когда ресурсы БД исчерпаны, рост числа соединений снижает производительность (основание для того, что в эксперименте с БД при нехватке ядер CPU увеличение пула замедляет всех) - [WAL Configuration (PostgreSQL Documentation)](https://www.postgresql.org/docs/current/wal-configuration.html) · PostgreSQL · Контрольная точка по умолчанию раз в 5 минут или после каждого 1 GB WAL пачкой записывает грязные страницы. Это дорогая операция, и запись распределяют, чтобы избежать всплеска 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 · Маршрутизатор должен уметь ограничивать частоту сообщений об ошибках ICMP, например Time Exceeded, и может ограничивать Echo Reply (это важно учитывать при чтении mtr и ping) - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · icmp_ratelimit и icmp_ratemask: Linux по умолчанию ограничивает ответы ICMP, например Time Exceeded и Destination Unreachable - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · Поля rtt (среднее время пути туда и обратно)/rttvar в ss -ti - [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 · Замер: одна очередь и одно ядро упираются примерно в 350 000–430 000 пакетов в секунду, а чтобы принимать 1 000 000 pps, нужно больше очередей и ядер (симуляция с запасом берёт 700 000 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 задаёт cpu.max - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · steal: время 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: TCP_NODELAY, TCP_USER_TIMEOUT, keepalive и др. - [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 · Быстрая повторная передача по 3 дублирующим ACK, после повторной передачи по таймеру окно перегрузки 1 сегмент (учебные правила в симуляции) - [RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP](https://www.rfc-editor.org/rfc/rfc8985) · IETF · RACK определяет потери по времени отправки вместо числа дублирующих ACK - [RFC 6298: Computing TCP's Retransmission Timer](https://www.rfc-editor.org/rfc/rfc6298) · IETF · Рекомендация: минимальный RTO 1 с, экспоненциальная задержка повтора с удвоением при каждом истечении - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · В Linux tcp_rto_min_us по умолчанию 200 ms, потери обнаруживает 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 · Отложенный ACK в Linux 40 ms в симуляции: TCP_DELACK_MIN (HZ/25 = 40 ms), максимум TCP_DELACK_MAX (HZ/5 = 200 ms) - [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 · Отложенный ACK в Windows 200 ms в симуляции: при получении данных ставится таймер отложенного ACK на 200 ms, и в сочетании с Nagle мелкие пакеты ждут ACK - [RFC 896: Congestion Control in IP/TCP Internetworks](https://www.rfc-editor.org/rfc/rfc896) · IETF · Исходная цель правила Нейгла: проблема удалённых терминалов, где на каждое нажатие клавиши (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 000 km в одну сторону 5 ms). До 150 ms в одну сторону почти незаметно для большинства приложений, но на сильно интерактивные задачи влияет даже задержка ниже 100 ms - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Масштаб интернет-участка по расположению сервера в эксперименте: от Сеула до региона Пусан 8 ms, Токио 30 ms, Сингапур 68 ms, запад США 124–136 ms, Европа 234–244 ms (медианы времени туда и обратно) - [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 в одну сторону: меньше 10 ms (без нагрузки, для мелких пакетов; в эксперименте для 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) в одну сторону: 4 ms (eMBB, без нагрузки) - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · Очередь роутера в эксперименте: у домашних роутеров при одновременной отдаче и загрузке задержка в очереди может вырасти до сотен ms (максимум около 400 ms) - [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 5 ms и интервал наблюдения 100 ms (очередь SQM около 5 ms в эксперименте) - [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 · Использовать роутер с поддержкой SQM вроде cake или fq_codel и подбирать скорость SQM, измеряя задержку под нагрузкой - [An Experimental Study of Home Gateway Characteristics (IMC 2010)](https://conferences.sigcomm.org/imc/2010/papers/p260.pdf) · ACM · Замеры 34 домашних роутеров: при одновременной отдаче и загрузке задержка в очереди до 400 ms, медиана времени жизни записи 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 · Требования к таймеру записи UDP в NAT (не меньше 2 минут, по умолчанию рекомендуется не меньше 5 минут) и обновление записи исходящими изнутри пакетами - [RFC 8325: Mapping Diffserv to IEEE 802.11](https://www.rfc-editor.org/rfc/rfc8325) · IETF · Wi-Fi работает по схеме CSMA/CA: если эфир занят, передача откладывается и выполняется после случайной задержки (backoff) - [Resolve Wi-Fi and Bluetooth issues caused by wireless interference](https://support.apple.com/en-us/102319) · Apple · Помехи от микроволновки в эксперименте: микроволновки, Bluetooth и т. п. мешают Wi-Fi 2,4 GHz, переход на 5 GHz помогает - [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: свет в оптоволокне проходит около 200 000 km в секунду, 1 000 km туда и обратно занимает минимум 10 ms - [The Internet at the Speed of Light (HotNets 2014)](https://conferences.sigcomm.org/hotnets/2014/papers/hotnets-XIII-final111.pdf) · ACM · Реальный путь через маршрутизаторы по медиане примерно в 1,5 раза длиннее прямой по оптоволокну, минимальный пинг в 3,2 раза больше расчёта по скорости света (примерно в 2 раза больше, чем по прямой по оптоволокну) - [Azure network round-trip latency statistics](https://learn.microsoft.com/en-us/azure/networking/azure-network-latency) · Microsoft Azure · Медианы измеренного времени туда и обратно от Сеула: Токио 30 ms, запад США 124–136 ms, Европа 234–244 ms (Корея–Европа примерно в 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 для обмена маршрутной информацией в интернете, рекомендуемое значение hold time по умолчанию 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 · Сквозная задержка в сети ЦОД меньше 1 ms, больше 70% всплесков заканчиваются за десятки µs, и по средней загрузке их не видно - [Data Center TCP (DCTCP) (SIGCOMM 2010)](https://conferences.sigcomm.org/sigcomm/2010/papers/sigcomm/p63.pdf) · ACM · В обычных коммутаторах несколько портов делят неглубокий буфер, и если на короткий момент много потоков сходятся в один порт, буфер переполняется и пакеты теряются - [Netfilter Conntrack Sysfs variables](https://docs.kernel.org/networking/nf_conntrack-sysctl.html) · Linux kernel · Максимальное число записей в таблице отслеживания соединений и время их жизни по умолчанию (UDP 30 с, поток UDP 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 минута в эксперименте: медиана времени жизни записи UDP в CGN 35 с в проводных сетях и 65 с в мобильных, у 74% измеренных NAT не больше 1 минуты, у NAT домашних роутеров (CPE) в основном 65 с - [tcp(7) — Linux manual page](https://man7.org/linux/man-pages/man7/tcp.7.html) · Linux man-pages · TCP keepalive в эксперименте: по умолчанию после 2 часов (7 200 с) простоя 9 проверок с интервалом 75 с, и если ответа нет, соединение рвётся - [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 · BFD, обнаруживающий отказ пути быстрее, чем секундные Hello протоколов маршрутизации - [RFC 2923: TCP Problems with Path MTU Discovery](https://www.rfc-editor.org/rfc/rfc2923) · IETF · Чёрная дыра MTU пути: ICMP заблокирован, и пропадают только большие пакеты ### #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 · Обращение к основной памяти 100 ns, путь туда и обратно внутри одного ЦОД 500 000 ns (0,5 ms) - [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 обновлений в секунду, интерполяция 100 ms. При 10 обновлениях в секунду интерполяция 200 ms переживает один пропуск - [IP Sysctl](https://docs.kernel.org/networking/ip-sysctl.html) · Linux kernel · tcp_rto_min_us по умолчанию 200000 (200 ms) - [Factors influencing the latency of simple reaction time (Frontiers in Human Neuroscience, 2015)](https://doi.org/10.3389/fnhum.2015.00131) · Frontiers · Среднее время простой реакции около 231 ms (213 ms с поправкой на задержку оборудования) - [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 · Даже задержка меньше 100 ms влияет на выполнение игровых задач, а в задачах вроде перетаскивания заметна даже задержка около 10 ms - [Peeking into VALORANT's Netcode](https://www.riotgames.com/en/news/peeking-valorants-netcode) · Riot Games · Опытные игроки в слепом тесте замечают разницу даже около 10 ms - [Latency and Player Actions in Online Games (Communications of the ACM, 2006)](https://web.cs.wpi.edu/~claypool/papers/precision-deadline/final.pdf) · ACM · Допустимая задержка по жанрам: от первого лица около 100 ms, от третьего лица (RPG, MMO) около 500 ms, RTS около 1 000 ms - [JEP 333: ZGC: A Scalable Low-Latency Garbage Collector](https://openjdk.org/jeps/333) · OpenJDK · G1 на куче 128 GB: пауза в среднем 157 ms и максимум 544 ms, у ZGC около 1–2 ms независимо от размера кучи и объёма живых данных - [CommonNetworkParametersExtensions (Unity Transport 2.5)](https://docs.unity3d.com/Packages/com.unity.transport@2.5/api/Unity.Networking.Transport.CommonNetworkParametersExtensions.html) · Unity · disconnectTimeoutMS: если за это время ничего не получено, соединение рвётся (по умолчанию 30 000 ms) ### #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 не отдаёт приложению даже уже пришедшие новые данные, пока не придёт повторная передача (обычно 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 lockstep к схеме клиент-сервер и клиентскому предсказанию - [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 · Lockstep: команды планируются на два хода вперёд, длина хода подстраивается под самый медленный компьютер, постоянная задержка 500 ms терпима, а скачущая мешает - [Deterministic Lockstep](https://gafferongames.com/post/deterministic_lockstep/) · Gaffer On Games · Lockstep продвигается, только когда пришли все входы, буфер задержки воспроизведения поглощает джиттер - [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 · Роллбэк убирает локальную задержку применения ввода в lockstep, пересчёт до 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 · Преимущество и нагрузка хоста listen-сервера - [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 · Если расходится один игрок, коррекция идёт только для него, а остальные девять видят плавную картину. Для проверки попадания с отмоткой назад задаётся предел - [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 · Хост listen-сервера в выигрыше перед другими игроками - [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-пакет пропадает целиком при потере хотя бы одного фрагмента - [Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE](https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse) · Microsoft · Если два сокета делят один порт, неизвестно, какой из них получит пакет - [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 с, удвоение при каждом истечении, потолок, если его вводят, не меньше 60 с, повторно переданные пакеты не используются как замеры RTT (Karn) - [RFC 5681: TCP Congestion Control](https://www.rfc-editor.org/rfc/rfc5681) · IETF · Быстрая повторная передача по третьему дублирующему 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 (если неподтверждённый пакет один, добавляется запас на отложенный ACK), сброс RTO после отправки TLP, 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 · Опции временных меток и масштабирования окна, без масштабирования окно не больше 64 KiB - [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 и шейпинг уменьшают переполнение очередей и bufferbloat - [RFC 1191: Path MTU discovery](https://www.rfc-editor.org/rfc/rfc1191) · IETF · MTU пути определяется по ICMP-уведомлению о превышении размера (тип 3, код 4) - [RFC 1812: Requirements for IP Version 4 Routers](https://www.rfc-editor.org/rfc/rfc1812) · IETF · Маршрутизаторы могут ограничивать частоту сообщений об ошибках ICMP (результат 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 (меньше 4 пакетов in-flight, до 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 200 ms, TCP_RTO_MAX 120 с, TCP_TIMEOUT_INIT 1 с, отложенный ACK 40–200 ms (TCP_DELACK_MIN и MAX), TCP_BASE_MSS 1 024, критерий thin stream (меньше 4 пакетов in-flight) - [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, если неподтверждённый пакет один, добавляется минимальный 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 · pacing_rate в BBR = 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 · Первые 4 шага backoff для SYN RTO не удваиваются (после первого RTO в 1 с ещё четыре ожидания по 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 · Опция сокета TCP_RTO_MIN_US, задающая минимальный RTO для каждого соединения (Linux 6.15) - [Android common kernels](https://source.android.com/docs/core/architecture/kernel/android-common) · Android (Google) · Поддерживаемые общие ядра Android 5.10 и новее (новее ядра 4.18, в котором RACK стал основным) - [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 через ограничение MSS в SYN - [ss(8) — Linux manual page](https://man7.org/linux/man-pages/man8/ss.8.html) · iproute2 · rto (ms), backoff (число экспоненциальных удвоений), rtt/rttvar и cwnd в ss -i - [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 · По строке на каждую повторную передачу с адресом, портом и состоянием, -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 · rx_out_of_buffer и rx_discards_phy у mlx5 - [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, шестнадцатеричные значения, 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 · Просмотр глобальных настроек TCP (Max SYN Retransmissions) через netsh int tcp show global, поиск потерь посередине маршрута одновременным захватом на обеих сторонах - [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 больше 10 ms), когда остаётся один пакет, TLP учитывает отложенный ACK 200 ms - [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 2 раза, начиная с 3 с, с удвоением ### #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 при отслеживании соединений в группе безопасности (350 с у типов инстансов Nitro v6, 5 дней у остальных, настраивается от 60 с до 5 дней), рекомендация слать keepalive чаще раза в 5 минут, для TCP через NLB 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 · Реальный размер очереди задаёт аргумент backlog в listen, а если он больше 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 · Расчёт заполнения conntrack по nf_conntrack_count и nf_conntrack_max - [Control Group v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) · Linux kernel · nr_throttled в cpu.stat: сколько раз срабатывал троттлинг по лимиту CPU контейнера - [proc_stat(5) — Linux manual page](https://man7.org/linux/man-pages/man5/proc_stat.5.html) · Linux man-pages · steal в /proc/stat: время, когда 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-тикового сервера кадр нужно закончить за 7,8125 ms, время кадра измеряют по подсистемам и делят бюджет между ними - [Introducing Time Dilation (TiDi)](https://www.eveonline.com/news/view/introducing-time-dilation-tidi) · CCP Games · Физическая симуляция EVE Online обновляется раз в секунду, при перегрузке игровые часы замедляются, и нагрузка, привязанная ко времени, пропорционально снижается - [HED-GP Technical Retrospective: What a HED-ache](https://www.eveonline.com/news/view/what-a-hed-ache) · CCP Games · Нижний предел Time Dilation 10%; рассылка O(n²), при которой о действии каждого из n игроков сообщается 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−f, ускорение не превышает 1/(1−f) - [Runtime locking correctness validator](https://docs.kernel.org/locking/lockdep-design.html) · Linux kernel · Дедлок в эксперименте с блокировками: если две блокировки берутся в противоположном порядке, возникает циклическое ожидание и дедлок - [Liveness, Readiness, and Startup Probes](https://kubernetes.io/docs/concepts/workloads/pods/probes/) · Kubernetes · Watchdog в эксперименте с блокировками: состояние дедлока обнаруживается проверкой живости (liveness) и устраняется перезапуском, по умолчанию проверка раз в 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 · Каскадный отказ: как медленный бэкенд удерживает потоки и ресурсы фронтенда, как сбой расползается через повторы, проваленные health check и перезапуски с пустым кэшем, и как с этим бороться - [Circuit Breaker Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) · Microsoft Azure · Состояния circuit breaker (закрыт, открыт, полуоткрыт) и порог по числу ошибок, при длинном таймауте потоки заняты, пока не сработает блокировка вызовов - [Bulkhead Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead) · Microsoft Azure · Изоляция ресурсов по функциям и вызываемым сервисам, чтобы сбой в одном месте не расползался - [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-сервера: входные серверы, серверы симуляции по ячейкам сетки (хабы), общий пул серверов для сессий, БД для хранения состояния - [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 · Watchdog в эксперименте со схемой серверов: службу, переставшую подавать признаки жизни, завершают и автоматически перезапускают ## Формы графиков - **Всплески с постоянным периодом** (`periodic`): Обычно значение низкое, но через равные промежутки подскакивает: каждые несколько секунд, минут или ровно в начале часа. - **Случайные всплески** (`random`): Значение подскакивает нерегулярно, без всякого интервала, и быстро возвращается. - **Ступенька вверх с определённого момента** (`step`): С конкретного момента (патч, изменение настроек, смена маршрута) значение поднимается на ступень и остаётся там. - **Плавный рост** (`ramp`): Значение понемногу растёт часами и днями. Чем дольше работа без перезапуска, тем оно выше. - **Пила: плавный рост и резкий сброс** (`sawtooth`): Значение медленно растёт, в момент перезапуска или очистки резко падает, и так раз за разом. - **Высоко только в определённые часы** (`peak`): В одни и те же часы суток, например в вечерний пик, график поднимается горбом. - **Растёт вслед за онлайном и нагрузкой** (`load`): Когда растёт онлайн или число игроков в одном месте, график поднимается ещё круче. - **Упор в лимит (плато)** (`ceiling`): Производительность или число соединений доходит до некоторого значения и дальше не растёт, а с этого момента растут ожидание и ошибки. - **Высоко с самого начала** (`high`): Значение без всплесков всё время держится высоким. Так бывает, когда причина в самой схеме: расстояние, маршрут, архитектура. - **Высоко только у некоторых** (`outlier`): У большинства в норме, высоко только у отдельных игроков, регионов, провайдеров или устройств. - **Провал, затем пачка** (`gap`): Какое-то время объём полученных данных равен 0, а потом всё прилетает разом. - **Массовый обрыв соединений** (`drop`): Число подключений резко падает, или число обрывов в один момент взлетает. - **Всплеск сразу после входа или техработ** (`surge`): Сразу после открытия сервера или старта события значение сильно подскакивает, потом постепенно спадает. ## Инструкции по ситуациям ### Лаги после патча Когда после определённого патча или деплоя стало больше сообщений о лагах. Подходит, если копятся жалобы вида «с этого обновления что-то не так» или если график с какого-то момента поднимается ступенькой и так и остаётся. 1. **Точно определить время начала и собрать все изменения до и после него**: Находят момент, когда пошёл первый поток сообщений о лагах, и момент, когда график поднялся ступенькой, и выписывают все изменения, выкаченные до и после них. Смотрят всё вместе: патч клиента, деплой сервера, изменения настроек, изменения схемы БД (DDL) и перезапуски, работы с сетью и файрволом, замену инфраструктуры (тип инстанса, ядро, драйверы). Если при каждом деплое ставить вертикальную линию на всех графиках с помощью аннотаций (annotation) в инструменте мониторинга, этот шаг проходит быстро. Если игровой патч и инфраструктурные работы вышли в одни и те же техработы, кандидатами остаются оба. Кого звать первым: обе команды, разработки и инфраструктуры, которые вносили изменения. (причины: in-deploy, so-os-update, db-ddl-lock, db-plan-flip, db-cold-cache) 2. **Разделить по охвату: сборка, устройство, сервер, регион**: Смотрят, в каком срезе сосредоточена проблема. Если плохо только у игроков на новой сборке, первым делом подозревают клиент; если только на определённой ОС, видеокарте или устройстве, то производительность клиента или драйверы; если только на определённом сервере, канале или в зоне, то сервер; если только в определённой стране или у определённого провайдера, то сетевой маршрут; если у всех одновременно, то общие ресурсы (БД, балансировщик нагрузки, шлюз) или только что выкаченный деплой сервера. Если в клиентской телеметрии есть номер сборки, ставят рядом пинг, FPS, всплески времени кадра и число дисконнектов на старой и новой сборке. Если пинг прежний, а упал только FPS, дело скорее в производительности клиента, чем в сети. Кого звать первым: если проблема сосредоточена в сборке или устройстве, команда разработки (клиент); если на сервере или канале, то при нормальных метриках хоста команда разработки (сервер), при отклонениях команда инфраструктуры (серверы и ОС); если в стране или у провайдера, команда инфраструктуры (сеть). (причины: 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. **Сравнить типы и число запросов к БД до и после**: Если выросла задержка БД, сначала смотрят, выросло ли вместе с ней число запросов (QPS). pg_stat_statements в PostgreSQL и сводка по digest в MySQL Performance Schema объединяют запросы, которые отличаются только значениями, в один тип и собирают по нему число выполнений и суммарное время. Поэтому если сравнить топ запросов до и после патча, видны новые запросы, запросы, число которых выросло в несколько раз (N+1), и запросы, которые читают всю таблицу без индекса (в MySQL столбец SUM_NO_INDEX_USED). Кого звать первым: если изменились QPS или вид запросов, команда разработки (сервер); если запросы те же, а выросла только задержка, команда инфраструктуры (БД: план выполнения, IOPS, блокировки). (причины: db-no-index, db-login-storm, db-plan-flip, db-cache-stampede) 6. **Определить слой по метрикам хоста и серверного процесса**: Без доступа к коду, по тому, что видно в ОС, отделяют проблемы внутри серверного процесса от проблем хоста. Если растёт очередь приёма серверного сокета (Recv-Q), серверный процесс не успевает читать данные (остановка тика, GC, блокировки). Если один поток загружен на 100%, это узкое место в однопоточном коде. Если в GC-логе выросло время пауз, изменился характер работы с памятью. Проверяют и то, не выкатили ли сборку с повышенным уровнем логирования, из-за чего выросла запись логов. Если же выросли CPU steal, троттлинг или дропы на NIC, смотрят, что в то же время поменялось в инфраструктуре (тип инстанса, ядро, лимиты контейнеров). Кого звать первым: если признаки внутри процесса, команда разработки (сервер); если признаки на хосте, команда инфраструктуры (серверы и ОС). (причины: 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 с зондов по всему миру, а можно поднять временную ВМ в регионе-кандидате и мерить с неё. Промежуточное оборудование иногда ограничивает ответы ICMP, поэтому по возможности меряют и тем же протоколом и портом, что и игра. Если трафик только одного провайдера идёт через подозрительно далёкий город, это проблема пиринга или маршрута. Провайдеры выбирают маршрут подешевле, даже если задержка на нём больше, поэтому трафик даже до близкой точки может идти далёким обходом. Кого звать первым: команда инфраструктуры (сеть), а если маршрут на стороне провайдера, внешние стороны (провайдер, IX). (причины: isp-distance, isp-routing, isp-peak, isp-cable) 2. **Сравнить замеры с пределами, на которые рассчитана игра**: Измеренные RTT и джиттер сравнивают с окнами реакции в игре (время на уклонение, парирование и т. п.), пределом компенсации задержки, длиной буфера интерполяции и размером буфера ввода. Например, если окно парирования 0,2 с, то абоненты провайдеров, у которых путь туда и обратно вместе с буфером интерполяции дольше этого, опаздывают, даже если реагируют вовремя. Если подогнать игру под них, расширив компенсацию задержки, то уже у тех, в кого попадают, становится больше жалоб «в меня попали, когда я уже был за стеной». Если провайдеров за пределом много, команда инфраструктуры прорабатывает размещение региона или точек присутствия (edge PoP) ближе к игрокам, а команда разработки пересматривает значения окон, интерполяции и компенсации задержки. Ориентиры собраны в главе справочника о моделях синхронизации. Кого звать первым: команда разработки (сервер и клиент: пределы архитектуры), команда инфраструктуры (сеть: размещение регионов и PoP). (причины: sy-short-window, sy-no-lagcomp, sy-lagcomp-overreach, cg-no-buffer) 3. **Проверить MTU и прохождение UDP**: Проверяют, проходит ли через местные сети целиком самый большой пакет игры. Отправляют ping с флагом запрета фрагментации (DF) пакетами разного размера, чтобы измерить MTU пути, и смотрят, нет ли участков с MTU меньше 1 500 байт, например PPPoE, туннелей или мобильной сети. Стандарт для датаграммных протоколов вроде UDP (RFC 8899) рекомендует для IPv4 базовый размер 1 200 байт, который проходит через большинство путей. Если максимальный пакет игры больше, вместе с командой разработки решают, уменьшать его или делить на части. Проверяют и то, не блокируют ли UDP или игровые порты и не ограничивают ли их скорость в публичном Wi-Fi, корпоративных сетях и у отдельных провайдеров, и есть ли запасной путь на случай блокировки (TCP, порт 443). Кого звать первым: команда инфраструктуры (сеть) и команда разработки (сервер: размер пакетов). (причины: dc-mtu, rt-mtu, sk-fragment, isp-udp-block, hn-captive, isp-shaping) 4. **Измерить таймаут простоя NAT и CGNAT и подобрать интервал хартбита**: Измеряют, через сколько местные домашние роутеры и мобильные сети (CGNAT) удаляют запись NAT для неактивного UDP-соединения. В каждом прогоне тестовое устройство отправляет на сервер один пакет, чтобы создать запись, и дальше ничего не шлёт, а сервер через заданное время (30 с, 60 с, 120 с …) отправляет пакет на устройство. Время, начиная с которого устройство перестаёт получать этот пакет, и есть таймаут простоя этой сети. Стандарт (RFC 4787) требует, чтобы запись UDP не истекала раньше чем через 2 минуты, и рекомендует по умолчанию 5 минут и больше, но значения у оборудования сильно различаются, и некоторые устройства удаляют записи раньше. Запись надёжно обновляется только пакетами, исходящими от устройства, поэтому хартбит отправляет клиент, и проверяют, что его интервал не больше половины самого короткого из значений: измеренного таймаута и таймаутов простоя балансировщика нагрузки и облачной группы безопасности. Кого звать первым: команда разработки (клиент: интервал хартбита; сервер: значения таймаутов), команда инфраструктуры (настройки балансировщика и групп безопасности). (причины: hn-nat, isp-cgnat, rt-mapping, dc-lb-idle, dc-cloud-conntrack) 5. **Проверить внешние сервисы и оборудование безопасности на пути местных игроков**: Проверяют, с нормальной ли скоростью отвечают местные сервисы входа через платформу, оплаты и подтверждения личности, правильно ли местные DNS разрешают адреса серверов авторизации и патчей и отдаёт ли CDN патчи из точки присутствия рядом с этой страной. Смотрят, не попадают ли диапазоны IP новой страны под правила блокировки по странам и ограничения скорости в защите от DDoS и файрволе, и особенно, не блокируются ли целиком диапазоны CGNAT, где один IP делят многие абоненты. Кого звать первым: команда инфраструктуры (оборудование безопасности, 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, первым делом смотрят маршрут этого провайдера (команда инфраструктуры, внешние стороны); если плохо во всей новой стране, то расстояние и пределы, на которые рассчитана игра (команда инфраструктуры, команда разработки); если хуже становится только вечером, то перегрузку пиринга. Если у части игроков пинг высокий постоянно, вместе с командой разработки (сервер) проверяют, не отправило ли их в далёкий регион из-за ошибки GeoIP, VPN или выбора региона по лидеру группы. Если синтетический мониторинг в норме, а плохо только у игроков, дело в окружении игрока или в клиенте. (причины: isp-peak, isp-routing, rt-queue-drop, pt-isp-validation, in-region-match) 7. **Проверить, как игроки из далёких регионов влияют на остальных**: Когда игроков, подключающихся издалека, становится больше, дело не ограничивается тем, что лагает у них самих. Ввод игрока с плохой связью приходит пачками, на экранах остальных его персонаж движется перемоткой, а на сервере срабатывают проверки скорости и кулдаунов, и появляются откидывание назад и отклонённые умения. В групповых механиках запоздалая реакция одного медленного игрока проваливает всю группу, а в lockstep все ждут самого медленного. После запуска в новой стране смотрят, не стало ли больше жалоб от прежних игроков вида «странно выглядит один персонаж», и вместе с командой разработки решают вопрос с буфером ввода, допусками проверок и разделением матчмейкинга по регионам. Кого звать первым: команда разработки (сервер). (причины: pt-slow-burst, pt-isp-validation, pt-raid-member, sy-lockstep) ## Реальные инциденты ### eve-hedgp-2014 · CCP Games 2014: EVE Online: перегрузка сервера во время масштабной битвы флотов в HED-GP - Что произошло: Постмортем от января 2014 года разбирает масштабную битву флотов в системе HED-GP, во время которой сервер был сильно перегружен. Time Dilation (функция, которая замедляет игровое время при перегрузке) дошла до нижнего предела в 10%, и всё поле боя ушло в слоумо, но нагрузка продолжала копиться. Отставание в обработке остановок и повторных срабатываний модулей (Dogma Lateness) доходило до 193 с игрового времени, то есть примерно до 32 минут реального. В битве за 6VDT в июле 2013 года, почти такой же по масштабу, максимум составлял 42 с (около 7 минут реального времени). - Причина: В CCP оговорились, что полной уверенности нет: инструменты профилирования сами добавляют нагрузку, поэтому в такой ситуации их не запускают. С этой оговоркой были названы две вероятные причины. Первая: бой затянулся, и необработанная нагрузка продолжала накапливаться. Вторая: стало больше дронов. Число выпущенных за бой дронов (без повторов) составило 21 123 в 6VDT и 38 852 в HED-GP, то есть на 84% больше. Когда о действии одного игрока нужно сообщить всем, кто его видит, объём рассылки растёт как квадрат числа участников (O(n²)), а у дронов на одну атаку приходится больше сообщений. К тому же код, которым дрон выбирает цель, часто перебирает все доступные для атаки цели на поле боя, и его стоимость растёт почти как n². - Выводы: Если в локации, где собралось много игроков, нагрузка превышает пропускную способность сервера, вся локация уходит в слоумо, а чем дольше идёт бой, тем больше копится отставание и тем сильнее задержка ввода. Признаки для проверки: время тика и объём отложенной работы на сервере (ноде), который обслуживает эту локацию, а также число игроков и объектов в ней. Характерно, что в других локациях всё в порядке. Основной ответственный: команда разработки (сервер). Что исправлять: круг получателей рассылки об одном действии и стоимость поиска целей у ИИ. Замедление игрового времени не убирает перегрузку, но все замедляются одинаково, и отдельные действия не застревают в очереди бесконечно. - Связанные причины: 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, должен был идти напрямую из Сан-Франциско в Портленд, но шёл через Лос-Анджелес, Денвер и Сиэтл, и вместо 14 ms по прямой занимал 70 ms. В Riot объясняют: когда маршрутизатор переполняется и отбрасывает пакеты, другие чемпионы скачут по экрану, а снаряды словно телепортируются. - Причина: В Riot назвали причинами маршруты и маршрутизаторы. Магистральные операторы и провайдеры отправляют трафик по самому дешёвому маршруту, даже если есть путь с меньшей задержкой, а когда выбранный по BGP маршрут делает большой крюк, растёт и число маршрутизаторов на пути. Нагрузка на маршрутизатор зависит от числа пакетов, их размер значения не имеет. Игровые пакеты весят около 55 байт, поэтому при том же объёме данных их в 27 раз больше, чем пакетов по 1 500 байт, и входные буферы маршрутизаторов они заполняют во столько же раз быстрее. По словам Riot, многие маршрутизаторы при перегрузке первыми отбрасывают UDP-пакеты. В качестве решения в Riot построили собственную сеть Riot Direct: маршрутизаторы в 10 крупных интернет-узлах США, напрямую связанные пирингом с максимально возможным числом провайдеров. Во второй части сказано, что доля игроков с пингом ниже 80 ms чуть больше чем за 9 месяцев выросла с 31% до 50%, а после переезда игровых серверов в Чикаго за одну ночь достигла 80%. - Выводы: Если даже внутри одной страны пинг заметно выше только у абонентов определённого провайдера, стоит подозревать маршрут. Признаки для проверки: распределение RTT по провайдерам (ASN) и транзитные города в выводе 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: Перегрузка edge-хоста на серверах League of Legends в Европе и Бразилии - Что произошло: В конце февраля 2020 года на серверах League of Legends EUW, EUNE и BR случилось несколько сбоев, и число новых матчей резко упало. Все бэкенд-сервисы, включая матчмейкинг и игровые серверы, были в нормальном состоянии, но входящего трафика почти не было. Чтобы не запускать турнирный режим (Clash) на кластерах, которые могли оказаться нестабильными, в Riot перенесли его на неделю. Длительность каждого сбоя в постмортеме не указана. - Причина: Совпали три вещи. Запрос к одному из сервисов был сформирован неправильно, в определённых случаях раз за разом завершался ошибкой и повторялся, и число запросов резко выросло. Из-за известной несовместимости системы контейнеров с версией ОС текла память внутри ОС. Обновление успели выкатить только примерно на 60% всей контейнерной среды Riot, а на кластерах Европы и Латинской Америки оно ещё шло. Edge-контейнеры, которые принимают интернет-трафик, фильтруют его и передают в бэкенд, разносились по разным хостам внутри одного шарда (группы серверов), но для разных шардов такого правила не было, поэтому во время каждого сбоя edge-контейнеры как минимум трёх шардов оказывались на одном хосте. На этот хост пришлась лавина повторных запросов, а утечка памяти его остановила. - Выводы: Если все бэкенд-сервисы отвечают «всё в норме, но трафик не приходит», смотрят на то, что стоит перед ними (edge, шлюз, балансировщик нагрузки). Признаки для проверки: перекос числа входящих соединений по хостам и доля ошибок и повторных попыток у определённого запроса. Основной ответственный: команда разработки (сервер: неправильный запрос и логика повторных попыток). Правила размещения контейнеров, обновление ОС и алерты на перекос берёт на себя команда инфраструктуры (серверы и ОС). В 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 часов: одна второстепенная БД остановила весь сервер - Что произошло: 22 января 2021 года сервер League of Legends EUW чуть больше 5 часов работал со сбоями. Метрики числа вошедших игроков и игроков в матчах разом оборвались, а между двумя перезапусками входов становилось больше, но матчи почти не начинались. - Причина: На основном сервере БД, которая обслуживала второстепенную функцию, случился аппаратный отказ, а автоматическое переключение этой БД на резервный сервер настроено не было. Пулы соединений у каждой БД были свои, но все они работали через один пул потоков. Задачи, отправленные в отказавшую БД, не завершались и занимали потоки, и в итоге потоки закончились у всей системы. Алерты сыпались потоком, и команда сначала подозревала недавнюю злонамеренную сетевую атаку и аппаратные работы в другом регионе, поэтому алерт по отказавшей БД заметили только примерно через час. Все системы работали в одной JVM, и когда после перезапуска под нагрузкой от переподключений GC останавливал процесс на несколько секунд, в сборе метрик тоже возникали большие пробелы. Очередь на вход к тому же не соблюдала заданный лимит, и приток игроков был неравномерным. - Выводы: Даже одна второстепенная БД, которую считали неважной, может остановить всё через общий ресурс вроде пула потоков. Признаки для проверки: число ожидающих запросов по каждой БД, загрузка пула потоков и слишком малое число начатых матчей по сравнению с числом входов. Ответственные: команда разработки (сервер: изоляция пулов потоков, таймауты) и команда инфраструктуры (БД: автоматическое переключение). Когда сыплются алерты, легко первым делом заподозрить недавнюю проблему (атаку и т. п.), поэтому причины исключают по одной в порядке диагностики (охват → момент → слой). После перезапуска проверяют и то, ограничивает ли очередь на вход приток игроков так, как настроено. - Связанные причины: 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 часа: конкуренция в кластере service discovery (Consul) - Что произошло: Сбой начался днём 28 октября 2021 года (по тихоокеанскому времени) с высокой загрузки CPU на одном сервере Consul. В 16:35 онлайн упал вдвое по сравнению с обычным, после чего встал весь сервис. Только 31 октября в 16:45 все игроки снова смогли войти, через 73 часа после начала сбоя. По данным Roblox, сервисом каждый день пользуются 50 миллионов человек. - Причина: Roblox использует HashiCorp Consul для service discovery (сервисы находят адреса друг друга), health check и как KV-хранилище, причём один кластер Consul обслуживал сразу несколько рабочих нагрузок. Первопричин было две. Первая: новую функцию streaming в Consul, которую несколько месяцев постепенно включали всё шире, накануне сбоя включили и для сервиса маршрутизации трафика, а число нод этого сервиса увеличили на 50%. При очень большом числе и чтений, и записей эта функция вызвала конкуренцию за один общий ресурс (Go channel). На двухсокетных (NUMA) серверах с большим числом ядер, которые поставили на замену прямо во время сбоя, конкуренция была ещё сильнее. Вторая: управление списком свободных страниц (freelist) в BoltDB, где Consul хранит лог Raft, патологически замедлилось, и при каждом добавлении записи до 16 kB на диск записывалось 7,8 MB. Медиана задержки записи в KV, обычно ниже 300 ms, выросла до 2 с, а на медленном сервере-лидере наблюдали и нулевое окно, когда буфер TCP заполнен до конца. Телеметрия зависела от Consul, поэтому вместе с ним пропали и метрики, нужные для поиска причины. - Выводы: Если тормозит базовая система, от которой зависят многие сервисы (service discovery, хранилище конфигурации, аутентификация), разом останавливаются все функции. Признаки для проверки: задержка записи, смены лидера и загрузка CPU в этой системе, а также изменения настроек прямо перед сбоем. Ответственные: обе команды, разработки (сервер) и инфраструктуры (серверы и ОС). Мониторинг нужно отделить от системы, за которой он следит, тогда метрики будут видны и во время сбоя. При восстановлении кэши пусты, и если впустить всех сразу, система может снова упасть, поэтому в 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 и ошибки очереди на вход - Что произошло: С начала раннего доступа к дополнению Endwalker в декабре 2021 года все миры были крайне перегружены. Очередь на вход росла, а при входе с экрана выбора персонажа и во время ожидания в очереди часто появлялась Error 2002. Случались и падения отдельных миров и зон (Error 3001), и таймауты очереди (Error 4004). Даже на момент объявления 11 декабря, на 8-й день раннего доступа, перегрузка продолжалась. - Причина: Error 2002 возникает в двух случаях. Первый: в очереди одного логического дата-центра больше 17 000 человек. Этот лимит не даёт очереди разрастись настолько, что упадёт сервер авторизации, и в этом случае клиент полностью закрывается. 7 декабря для лобби-серверов задействовали резервное оборудование, предназначенное для разработки, и подняли лимит. Этих ошибок стало меньше, но очередь, наоборот, выросла. Второй случай: нестабильное подключение у игрока, ожидающего в очереди. Ожидание затянулось, и короткие обрывы связи из-за потерь пакетов на интернет-маршруте или нестабильного Wi-Fi стали случаться чаще. Лобби-сервер ждёт переподключения от нескольких десятков секунд до минуты. Если игрок успевает переподключиться, он продолжает с того же места в очереди, а если нет, встаёт в самый конец. По словам Square Enix, большинство обращений относилось именно к этому случаю. Быстро добавить миры мешал и дефицит полупроводников. - Выводы: Чем длиннее очередь, тем чаще короткий обрыв связи у ожидающего игрока превращается в ошибку подключения. При одной и той же перегрузке ошибки достаются в основном тем, кто играет через Wi-Fi или нестабильное подключение, и проблема становится проблемой «только у части игроков». Признаки для проверки: длина очереди и время ожидания, а также доля обрывов во время ожидания среди причин отключения. Основной ответственный: команда разработки (сервер: лимит очереди и окно переподключения). Добавление лобби-серверов и серверов миров вместе с ней берёт на себя команда инфраструктуры. Если оставить щедрое окно переподключения, короткие обрывы на линии игрока реже оборачиваются потерей места в очереди. - Связанные причины: in-login-queue, hn-wifi, rt-wireless - Первоисточник: [Square Enix](https://na.finalfantasyxiv.com/lodestone/news/detail/6a94b30182b6d963994fdc0b789264ac9f24986f) ### cloudflare-2020 · Cloudflare 2020: Потеря трафика в части городов из-за ошибки в настройке магистрали Cloudflare - Что произошло: Многие игры держат сайт, API и защиту от DDoS у CDN-провайдеров, поэтому такой инфраструктурный сбой задевает и игры. 17 июля 2020 года с 21:12 до 21:39 (UTC), за 27 минут, общий трафик в сети Cloudflare упал примерно на 50%. Пострадали только точки присутствия в части городов США, Европы, России и Бразилии, подключённые к магистрали, остальные работали нормально. - Причина: Из-за аварии на магистральном участке Ньюарк–Чикаго перегрузился участок Атланта–Вашингтон, и инженер изменил настройку маршрутизатора, чтобы снять с Атланты часть магистрального трафика. Отключить нужно было весь элемент политики (term), однако отключили только условие внутри него (prefix-list). В результате маршрутизатор в Атланте разослал по всей магистрали все маршруты BGP с более высоким приоритетом (local-preference 200). Маршрутам к собственным серверам точки присутствия давали приоритет 100, поэтому весь трафик точек, подключённых к магистрали, ушёл в Атланту. Атланта перегрузилась, а у пострадавших точек почти не осталось трафика для обработки. Когда маршрутизатор в Атланте отключили от магистрали, работа восстановилась. В Cloudflare заявили, что сбой не связан с атакой или взломом. - Выводы: Если только у игроков определённого города или региона разом начинаются дисконнекты или ошибка входа / бесконечная загрузка, а у остальных всё в порядке, первым делом подозревают недавнее изменение маршрутизации. На графике CPU и трафик взлетают только в одной точке присутствия, а в пострадавших, наоборот, падают почти до нуля. Основной ответственный: команда инфраструктуры (сеть), а если сбой на стороне самого провайдера, то внешние стороны. В Cloudflare решили ограничить число маршрутов, принимаемых в магистральных сессиях BGP (maximum-prefix), и изменили приоритеты так, чтобы одна точка не могла перетянуть на себя трафик других. - Связанные причины: isp-bgp, rt-path - Первоисточник: [Cloudflare](https://blog.cloudflare.com/cloudflare-outage-on-july-17-2020/) ### fastly-2021 · Fastly 2021: Ошибки CDN Fastly по всему миру - Что произошло: Многие игры раздают файлы патчей, лаунчер и веб-страницы через CDN, поэтому такой инфраструктурный сбой задевает и игры. 8 июня 2021 года с 09:47 (UTC) 85% сети Fastly начали возвращать ошибки. За 49 минут нормальная работа вернулась на 95% сети, а в 12:35 сбой был устранён. - Причина: В деплое ПО, начатом 12 мая, был баг, который срабатывал, когда определённая конфигурация клиента Fastly встречалась с определёнными условиями. 8 июня один из клиентов загрузил вполне корректное изменение конфигурации, и условия совпали. Fastly обнаружила аномалию за 1 минуту, а когда вызвавшую сбой конфигурацию клиента нашли и отключили, началось восстановление. Деплой исправления начали в тот же день в 17:25. - Выводы: Даже код, задеплоенный несколько недель назад, при редком стечении условий в одно мгновение превращается в глобальный сбой. Признаки для проверки на стороне игры: одновременный рост доли HTTP-ошибок у запросов к патчам, лаунчеру и сайту во всех регионах и статусная страница CDN-провайдера. Характерно, что уже установленные игровые соединения, если они идут мимо CDN, продолжают работать, и блокируются только новые подключения, загрузка патчей и вход через веб. Основной ответственный: внешние стороны (CDN-провайдер). Команда разработки и команда инфраструктуры заранее готовят обходной путь: используют два CDN и больше или раздают файлы напрямую с origin-сервера. - Связанные причины: in-external - Первоисточник: [Fastly](https://www.fastly.com/blog/summary-of-june-8-outage) ### meta-2021 · Meta 2021: Сбой Facebook: из-за одной команды на магистральных маршрутизаторах пропал даже DNS - Что произошло: Инфраструктурный сбой, уроки которого напрямую применимы к собственной сети и DNS игровой компании. 4 октября 2021 года сервисы Facebook (сейчас Meta) стали недоступны по всему миру. Магистраль, соединяющая ЦОД, полностью отключилась, и из интернета стало невозможно найти DNS-серверы Facebook. Длительность сбоя в постмортеме не указана. - Причина: Во время планового обслуживания команда, отданная для оценки ёмкости магистрали по всему миру, вопреки замыслу разорвала все соединения магистрали, а инструмент аудита, который должен был блокировать такие команды, из-за бага этого не сделал. 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 - Что произошло: Многие игры держат серверы, вход и данные в публичном облаке, поэтому такой инфраструктурный сбой задевает и игры. 7 декабря 2021 года в 7:30 (тихоокеанское стандартное время) перегрузилась внутренняя сеть региона Северная Вирджиния (us-east-1). С 7:33 выросли ошибки и задержки EC2 API, и запускать новые инстансы стало трудно (запуск инстансов восстановился в 14:40). Затем последовали сбои входа в консоль, невозможность менять настройки Route 53, задержки и частичная потеря метрик CloudWatch. Сетевое оборудование полностью восстановилось в 14:22. Уже работавшие инстансы EC2 и ответы DNS по существующим записям затронуты не были. - Причина: Автоматическая задача по наращиванию ёмкости одного из сервисов в основной сети вызвала неожиданное поведение у множества клиентов во внутренней сети, и число попыток подключения резко выросло. Оборудование, соединяющее внутреннюю и основную сети, захлебнулось, связь стала запаздывать, а задержки, в свою очередь, увеличивали число попыток подключения и повторов, и перегрузка не спадала. У клиентов был механизм backoff, который при такой перегрузке увеличивает интервал между запросами, но из-за скрытого дефекта он не сработал как надо. Внутренний мониторинг тоже зависел от этой сети, и команда эксплуатации работала без метрик в реальном времени, опираясь на логи. - Выводы: Если повторные попытки не увеличивают интервал, короткая перегрузка превращается в сбой на несколько часов. Для игры это значит, что уже работающие игровые серверы в порядке, но добавление новых серверов (автомасштабирование), вход, матчмейкинг и оплата через облачные API, а также мониторинг могут встать одновременно. Признаки для проверки: статусная страница облачного провайдера, доля ошибок облачных API и неудачные запуски инстансов. Основной ответственный: внешние стороны (облачный провайдер). Команда разработки добавляет ко всем повторным попыткам экспоненциальную задержку повтора (backoff) со случайным разбросом и лимит числа попыток, а команда инфраструктуры держит запас ёмкости на случай, если масштабирование заблокировано, и готовит запасной вариант в другом регионе. - Связанные причины: in-cascade, in-autoscale, in-external - Первоисточник: [AWS](https://aws.amazon.com/message/12721/) ### cloudflare-dns-2025 · Cloudflare 2025: Сбой публичного DNS Cloudflare 1.1.1.1 - Что произошло: Сбой публичного DNS-резолвера, который пользователи сами прописывают в устройстве или роутере. При таком сбое все игры и сервисы разом перестают работать только у тех, кто использует эту настройку. 14 июля 2025 года с 21:52 до 22:54 (UTC), 62 минуты, резолвер 1.1.1.1 не отвечал по всему миру. В Cloudflare отметили, что для многих пользователей это фактически означало недоступность любых интернет-сервисов. Пострадали запросы по UDP, TCP и DNS over TLS, а DNS over HTTPS, к которому подключаются по доменному имени, работал сравнительно стабильно. - Причина: 6 июня, при подготовке сервисной топологии (конфигурации, которая определяет, из каких точек присутствия анонсировать диапазоны IP) для другого, ещё не запущенного сервиса, в неё по ошибке попали и диапазоны IP резолвера 1.1.1.1. 14 июля конфигурацию этого сервиса изменили, и вместо всех точек присутствия диапазоны резолвера стала анонсировать одна-единственная точка, к тому же отключённая. По всему миру маршруты BGP были отозваны. Изменение не прошло через канареечный деплой и сразу распространилось на все ЦОД. В 22:20 прежнюю настройку вернули, и трафик восстановился примерно до 77%, но за это время примерно на 23% edge-серверов стёрлись нужные настройки IP. Их пришлось задавать заново, и нормальная работа вернулась только в 22:54. В Cloudflare заявили, что это внутренняя ошибка конфигурации, не связанная с атакой или перехватом маршрутов BGP (hijacking). - Выводы: Если игровые серверы и другие игроки в порядке, а у части игроков ошибка входа / бесконечная загрузка при подключении к серверам авторизации и патчей, подозревают 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: Сбой DNS DynamoDB в AWS us-east-1 и долгое восстановление - Что произошло: Многие игры держат серверы, вход и данные в публичном облаке, поэтому такой инфраструктурный сбой задевает и игры. С 23:48 19 октября до 14:20 20 октября 2025 года (тихоокеанское летнее время) в регионе Северная Вирджиния последствия шли в три этапа. До 2:40 20 октября росли ошибки DynamoDB API. С 2:25 до 10:36 не запускались новые инстансы EC2 (проблемы со связью у части новых инстансов ушли в 13:50). С 5:30 до 14:09 росли ошибки подключения у части Network Load Balancer (NLB). - Причина: В автоматике, которая управляет DNS DynamoDB, было скрытое состояние гонки (race condition). Один из исполнителей, применяющих DNS-планы в разных зонах доступности (DNS Enactor), сильно запоздал и перезаписал новый план устаревшим. Сразу после этого очистка, запущенная другим исполнителем, удалила этот устаревший план, и DNS-запись регионального эндпоинта (dynamodb.us-east-1.amazonaws.com) стала пустой. Автоматика не смогла это исправить, и восстанавливать пришлось вручную. Система управления физическими серверами EC2 зависит от DynamoDB, поэтому за это время истекли аренды (lease), которые поддерживались для каждого физического сервера. Когда DynamoDB вернулась, физических серверов оказалось так много, что повторное получение аренды не успевало завершиться до таймаута, повторные задачи снова копились, и система попала в состояние «коллапса от перегрузки» (congestive collapse). Сетевые настройки новых инстансов распространялись с опозданием, health check NLB то проходили, то нет, и даже исправные ноды раз за разом выпадали из DNS и возвращались. - Выводы: Ошибка в DNS-записи одного сервиса перекидывается на другие сервисы, которые от него зависят, и даже после устранения причины восстановление занимает ещё несколько часов из-за накопившихся задач и нестабильных health check. Для игры это значит, что уже работающие серверы держатся, но новые серверы не запускаются и автомасштабирование встаёт, а при нестабильных health check балансировщик выводит из ротации исправные серверы. Признаки для проверки: статусная страница облака, доля ошибок API управляемых сервисов, неудачные запуски инстансов и число исправных целей у балансировщика нагрузки. Основной ответственный: внешние стороны (облачный провайдер). Команда инфраструктуры ограничивает число серверов, которые могут разом выпасть из-за проваленных health check, и готовит запасной вариант в другом регионе. - Связанные причины: in-external, in-cascade, in-autoscale, dc-lb-imbalance, isp-dns - Первоисточник: [AWS](https://aws.amazon.com/message/101925/) ## Термины - **Пинг** (Ping, RTT): Время, за которое ваш сигнал доходит до сервера и возвращается (путь туда и обратно). В пинг, который показывает игра, иногда входит и время ожидания обработки на сервере. - **Задержка** (Latency): Время от отправки пакета до его прибытия. Часто имеют в виду одно направление, поэтому это примерно половина пинга. - **Джиттер** (Jitter): Неравномерность интервалов между приходом пакетов. Даже при одинаковом среднем пинге большой джиттер даёт микрофризы. - **Пакет** (Packet): Порция данных, которую сеть передаёт за один раз. Обычно не больше 1 500 байт, а игровые обновления занимают от десятков до сотен байт. - **Потери пакетов** (Packet loss): Отправленный пакет не доходит и пропадает. В игре на TCP уже при 1% потерь картинка заметно спотыкается раз в несколько секунд или раз в десяток с лишним секунд, а игры на UDP с интерполяцией и повторной отправкой ввода могут скрывать даже потери в несколько процентов. - **Пропускная способность** (Bandwidth): Максимальный объём данных, который линия связи может передать за 1 секунду (Mbps). Не путайте с задержкой: она показывает, как быстро данные доходят. - **Тик** (Tick): Один шаг, за который сервер рассчитывает состояние игры. 20-тиковый сервер считает 20 раз в секунду, каждые 50 ms. - **Тикрейт** (Tick rate): Сколько тиков сервер выполняет за секунду. Чем выше, тем быстрее отклик, но растут стоимость серверов и объём трафика. Чтобы экономить трафик, частоту отправки пакетов иногда делают ниже тикрейта. - **Бюджет тика** (Tick budget): Предел времени, за который нужно завершить один тик. Если его превысить, следующий тик запаздывает и интервал между тиками растёт. - **FPS** (Frames per second): Сколько раз в секунду обновляется изображение. При 60 FPS на кадр приходится 16,7 ms. - **Время кадра** (Frame time): Сколько времени ушло на отрисовку одного кадра. Для ощущений редкие долгие кадры важнее среднего FPS. - **Снапшот** (Snapshot): Сводка «текущего состояния игры», которую сервер отправляет каждый тик: позиции, здоровье, состояния и т. п. Обычно в ней только то, что изменилось относительно уже известного получателю состояния (дельта-сжатие). - **Интерполяция** (Interpolation): Отрисовка промежуточных положений между двумя полученными снапшотами, чтобы движение выглядело плавным. Цена: на экране показывается немного прошлое. - **Буфер интерполяции** (Interpolation buffer): Время, на которое отрисовку намеренно откладывают ради интерполяции. Этот запас поглощает джиттер и одну-две потери. Обычно он равен 2 интервалам между пакетами (100 ms, если пакеты приходят 20 раз за 1 с), а некоторые игры сами увеличивают его, когда растёт джиттер. - **Экстраполяция** (Extrapolation, Dead reckoning): Когда новых пакетов нет, игра по последней скорости угадывает, где объект окажется дальше, и рисует его там. Если прогноз не сбылся, это выглядит как телепортация, поэтому многие игры экстраполируют примерно до 0,25 с и останавливаются (значение по умолчанию в движке Source: 0,25 с). - **Клиентское предсказание** (Client-side prediction): Игра сразу двигает вашего персонажа, не дожидаясь подтверждения сервера. - **Серверная коррекция** (Reconciliation): Когда приходит результат от сервера, клиент сравнивает его с предсказанием и исправляет позицию персонажа: берёт подтверждённую сервером позицию и заново применяет ещё не подтверждённый ввод. При большом расхождении это выглядит как откидывание назад. - **Компенсация задержки** (Lag compensation): При проверке попадания сервер отматывает время назад, к моменту, который видел атакующий, и проверяет, было ли попадание. Чтобы не наказывать тех, в кого стреляют, глубину отмотки ограничивают. В соревновательных шутерах обычно около 0,2–0,25 с, но бывает и до 1 с, как по умолчанию в движке Source. - **Авторитетный сервер** (Authoritative server): Архитектура, в которой окончательное решение принимает только сервер. Это защищает от читов, но каждый результат требует пути до сервера и обратно. Поэтому ожидание маскируют предсказанием и опережающим фидбеком. - **Lockstep** (Deterministic lockstep): Все участники обмениваются только вводом и на одном и том же ходе одинаково всё рассчитывают. Задают фиксированную задержку применения ввода (input delay), а если чей-то ввод опаздывает, ждут все. - **Серверный буфер ввода** (Server-side input buffer): Буфер, в котором сервер копит немного ввода от каждого игрока и берёт по одной команде за тик. Даже игрок с большим джиттером выглядит для других плавно, но его действия подтверждаются на сервере на это время позже. - **Listen-сервер** (Listen server): Схема, в которой ПК одного из игроков одновременно и играет, и работает сервером. У хоста пинг равен 0, но если его подключение или ПК медленные, лагает у всех. - **Фазирование** (Phasing): Функция, которая в одном и том же месте показывает разных NPC и разный ландшафт в зависимости от прогресса по квестам. Если у двух персонажей разный прогресс, отсутствие NPC у одного из них нормально. - **Роллбэк-неткод** (Rollback netcode (GGPO)): Игра предсказывает ввод соперника и идёт дальше, а если реальный ввод оказался другим, возвращается к прошлому кадру и пересчитывает. Часто используется в файтингах. С роллбэком в БД это не связано. - **Буферизация ввода** (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): Один застрявший элемент в начале очереди заставляет ждать всех за ним. Причина перемотки в играх на TCP. - **RTO** (Retransmission timeout): Таймер повторной передачи: сколько TCP ждёт, прежде чем признать пакет потерянным и отправить его снова. В Linux не меньше, чем пинг + 200 ms, после каждой неудачи удваивается. - **Алгоритм Нейгла** (Nagle’s algorithm): Функция TCP, которая копит мелкие данные, пока не придёт подтверждение (ACK) на отправленные ранее, и отправляет их одним пакетом, чтобы экономить пакеты. В играх его обычно нужно отключать. - **TCP_NODELAY** (TCP_NODELAY): Опция сокета, которая отключает алгоритм Нейгла. Мелкие сообщения уходят сразу. - **Отложенный ACK** (Delayed ACK): Функция, при которой подтверждение приёма отправляется с небольшой задержкой, вместе с другими данными. В Linux обычно 40 ms (максимум 200 ms), в старых версиях Windows 200 ms, в современных 40 ms. - **Буфер сокета** (SO_SNDBUF / SO_RCVBUF): Размер места, которое ОС выделяет каждому сокету под данные, ожидающие отправки или чтения. Слишком маленький буфер переполняется, а в слишком большом копятся устаревшие данные и ждут своей очереди. - **keepalive** (SO_KEEPALIVE): Функция TCP, которая проверяет, живо ли неактивное соединение. По умолчанию выключена, а если её включить, со стандартными настройками первая проверка будет только через 2 часа. - **RST** (TCP reset): Сигнал TCP, который немедленно и принудительно закрывает соединение. Ещё не отправленные данные пропадают. - **Хартбит** (Heartbeat): Сигнал «я жив», который игра сама отправляет с постоянным периодом. Нужен, чтобы обнаруживать оборванные соединения и не давать промежуточному оборудованию закрыть соединение. - **Таймаут** (Timeout): Сколько ждать ответа, прежде чем считать попытку неудачной. Слишком короткий таймаут даёт ложные срабатывания, слишком длинный запаздывает с обнаружением. - **NAT** (Network Address Translation): Функция роутера, которая выпускает несколько домашних устройств в интернет с одного публичного IP и записывает соединения в таблицу NAT. - **CGNAT** (Carrier-grade NAT): Крупномасштабный NAT у провайдера, при котором один IP делят несколько абонентов. - **MTU** (Maximum Transmission Unit): Максимальный размер пакета, который можно отправить за один раз. Обычно 1 500 байт, на участках с VPN или PPPoE меньше. - **Bufferbloat** (Bufferbloat): Оборудование копит слишком длинные очереди, и задержка вырастает до сотен ms. - **SQM** (Smart Queue Management (fq_codel, CAKE)): Функция роутера, которая держит очереди короткими и справедливо распределяет отправку между потоками. Решение проблемы bufferbloat. - **QoS** (Quality of Service): Функция, которая даёт приоритет важному трафику, чтобы он уходил первым. - **Пиринг** (Peering): Точки, где сети провайдеров соединяются друг с другом. По вечерам легко перегружаются. - **BGP** (Border Gateway Protocol): Протокол, по которому провайдеры сообщают друг другу, каким маршрутом отправлять трафик в интернете. Когда маршруты меняются, меняются путь пакетов и пинг. - **DDoS** (Distributed Denial of Service): Атака, при которой огромный объём трафика со множества источников выводит сервис из строя. - **Центр очистки трафика** (DDoS scrubbing center): Узел компании, которая защищает от DDoS: во время атаки трафик к серверу сначала идёт туда, атака отсеивается, и дальше передаётся только нормальный трафик. Если узел далеко, маршрут удлиняется. - **Файрвол** (Firewall): Устройство или программа, которая пропускает только разрешённые соединения. Отслеживает соединения в таблице сессий. - **Балансировщик нагрузки** (Load balancer): Устройство, которое распределяет входящие соединения между несколькими серверами. - **Таблица сессий** (Session table, conntrack): Таблица, в которой оборудование или ОС отслеживает текущие соединения. Её размер ограничен. - **Микробёрст** (Microburst): В среднем трафика немного, но в очень короткие моменты (1 ms и меньше) он резко наваливается. - **NIC** (Network Interface Card): Сетевая карта сервера. - **Кольцевой буфер** (Ring buffer): Буфер, где NIC держит полученные пакеты, пока их не заберёт CPU. Фиксированное число слотов используется по кругу, и когда все слоты заняты, новые пакеты отбрасываются. - **Прерывание** (Interrupt): Сигнал, которым устройство сообщает CPU, что появилась работа. - **RSS** (Receive Side Scaling): Функция NIC, которая раскладывает полученные пакеты по нескольким очередям приёма, чтобы их обрабатывали несколько ядер CPU. - **PPS** (Packets per second): Число пакетов в секунду. Игровые серверы часто упираются в этот показатель раньше, чем в пропускную способность. - **Ядро** (Kernel): Центральная часть операционной системы. Отвечает за сеть, память и распределение CPU. - **backlog** (Listen backlog): Очередь, в которой ждут новые запросы на подключение, ещё не принятые сервером. Когда очередь заполнена, Linux молча отбрасывает новые запросы, а Windows отвечает отказом. - **TIME_WAIT** (TIME_WAIT): Состояние, в котором сторона, первой закрывшая соединение, ещё некоторое время (в Linux 60 с) держит эту комбинацию портов на случай запоздавших пакетов. - **CPU steal** (Steal time): Время, которое виртуальная машина прождала, потому что физический сервер отдавал CPU другим виртуальным машинам. Видно в top как значение st. - **Троттлинг CPU** (CFS throttling): Принудительная остановка контейнера до следующего периода, если он израсходовал квоту CPU (quota) внутри заданного периода (CFS period, обычно 100 ms). - **Файловый дескриптор** (File descriptor): Номер (fd), который получает каждый открытый процессом файл или соединение. Их количество ограничено. - **Поток** (Thread): Единица выполнения внутри программы, которая работает независимо. Несколько потоков могут выполняться одновременно. - **Переключение контекста** (Context switch): CPU снимает выполняющийся поток и ставит на его место другой. Это не бесплатно. - **Блокировка** (Lock, Mutex): Механизм, который позволяет работать с общими данными только одному потоку за раз. - **Дедлок** (Deadlock): Состояние, в котором потоки ждут блокировок, захваченных друг другом, и стоят вечно. - **Пул потоков** (Thread pool): Заранее созданный набор рабочих потоков. Если все заняты, новая работа ждёт. - **Асинхронный ввод-вывод** (epoll, IOCP, io_uring): Подход, при котором программа не ждёт окончания ввода-вывода, занимается другой работой и получает уведомление о завершении. - **AOI** (Area of Interest): Зона, которую «видит» каждый игрок. Отправляются только изменения внутри неё, и трафик сокращается. Чтобы дешевле определять, кто попадает в зону, карту обычно делят на сетку и проверяют только ближние ячейки. - **Рассылка** (Broadcast, fan-out): Отправка одного изменения всем, кто может его видеть. Если все собравшиеся видят друг друга, объём отправки растёт пропорционально квадрату числа игроков. - **GC** (Garbage collection): Автоматическое освобождение памяти, которая больше не используется. Во время GC программа может останавливаться. - **Куча** (Heap): Область памяти, которую программа по ходу работы запрашивает по мере необходимости. - **Утечка памяти** (Memory leak): Ошибка, при которой ненужная память не возвращается и её потребление растёт без конца. Бывает и при наличии GC, если на ненужные объекты где-то сохраняются ссылки. - **Своп** (Swap, paging): Когда не хватает RAM, часть памяти выгружается на диск. Обращение к выгруженной памяти более чем в 1 000 раз медленнее, чем к RAM. - **OOM killer** (Out-of-memory killer): Механизм Linux: когда память заканчивается, он выбирает процесс, который занимает больше всего памяти, и принудительно завершает его. В контейнере срабатывает, как только достигнут лимит памяти. - **Промах кэша** (Cache miss): Нужных данных нет в ближнем к CPU кэше, и приходится идти в медленную память. - **IOPS** (I/O operations per second): Сколько операций чтения и записи диск выполняет за 1 секунду. У облачных дисков лимит зависит от оплаченного уровня. - **fsync** (fsync): Вызов, который ждёт, пока данные гарантированно запишутся на диск. Обычная запись сначала попадает в память ОС и уходит на диск позже, и если за это время у сервера пропадёт питание, данные могут потеряться. fsync надёжен, но медленный. - **Burst-кредиты** (Burst credits): Накопленный запас, который позволяет облачному диску или серверу ненадолго работать быстрее базового уровня. Когда запас кончается, производительность падает до базовой. - **Индекс** (Index): Структура в БД для быстрого поиска строк. Без неё приходится читать всю таблицу. - **Полное сканирование** (Full table scan): Запрос, который без индекса проверяет каждую строку таблицы. - **План выполнения** (Query plan): Способ, которым БД решила выполнить запрос: в каком порядке и с какими индексами. Даже при неизменном коде тот же запрос может внезапно замедлиться, если БД сменит план. - **Транзакция** (Transaction): Набор операций в БД, объединённый по принципу «всё или ничего». Обмен предметами обязательно проводят в транзакции. Пока она не завершится, изменённые строки заблокированы, поэтому чем она короче, тем лучше. - **Пул соединений** (Connection pool): Набор заранее открытых соединений с БД. Если все заняты, новый запрос ждёт. - **Горячая строка** (Hot row): Строка, которую одновременно пытаются изменить много запросов. Источник конкуренции за блокировки. - **Отставание репликации** (Replication lag): Насколько реплика БД отстаёт от основной БД. - **Роллбэк** (Rollback): Сохранение отменяется, и данные возвращаются к прежнему состоянию. Игрок воспринимает это как «пропал предмет». - **Кэш** (Cache (Redis etc.)): Копия часто используемых данных в быстром хранилище. Снижает нагрузку на БД. - **Контрольная точка** (Checkpoint): Периодическая запись накопленных в памяти изменений БД на диск разом. В этот момент сохранение и чтение могут ненадолго замедлиться. - **Переключение на резерв** (Failover): Переход на резервный сервер или БД, когда основной вышел из строя. Пока идёт переключение, сохранения ненадолго недоступны, а если репликация отставала, последние данные могут потеряться. - **MVCC** (Multi-version concurrency control): Способ, которым БД не даёт читающим и пишущим мешать друг другу: старые версии строк временно хранятся. Если долго висит открытая транзакция, старые версии копятся и всё замедляется. - **Cache stampede** (Cache stampede): Кэш разом пустеет, и запросы лавиной идут к источнику (БД). - **Шлюз** (Gateway): Промежуточный сервер, который принимает подключения клиентов и передаёт их игровым серверам за собой. - **Circuit breaker** (Circuit breaker): Механизм, который на время прекращает вызовы сервиса, раз за разом отвечающего ошибками, и сразу возвращает ошибку, чтобы не допустить каскадного отказа. Через некоторое время делает один-два пробных вызова и, если сервис восстановился, снова разрешает вызовы. - **Каскадный отказ** (Cascading failure): Сбой в одном месте распространяется по цепочке вызовов на другие сервисы. - **Автомасштабирование** (Autoscaling): Функция, которая автоматически добавляет и убирает серверы в зависимости от нагрузки. На добавление нужно время. - **Watchdog** (Watchdog): Таймер, который следит, не завис ли сервер. Если игровой цикл стоит дольше заданного времени (от нескольких до десятков секунд), watchdog сохраняет снимок состояния (дамп) и принудительно завершает сервер, чтобы его перезапустили. - **Утилизация** (Utilization): Доля времени, когда воркер (то, что обрабатывает запросы: ядро CPU, поток, соединение с БД) занят. Выше 80–90% ожидание резко растёт. - **p99** (99th percentile): Значение, быстрее которого проходят 99 случаев из 100, а медленнее примерно 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): Центральный поток игры, который по очереди считает игровую логику и готовит кадр. Если здесь хоть одна операция длится долго, на это время картинка замирает. - **Разрешение таймера** (Timer resolution): Минимальный интервал, через который ОС может разбудить уснувшую программу. В Windows по умолчанию 15,6 ms, поэтому, если программа не изменит его сама, даже просьба «разбуди через 1 ms» выполнится позже. - **Тепловой троттлинг** (Thermal throttling): Защитная функция: при нагреве устройство само снижает частоты CPU и GPU. На телефонах обычно начинается через несколько минут или несколько десятков минут игры. - **VRAM** (Video memory): Собственная память видеокарты. В неё загружают текстуры и модели для отрисовки. Если её не хватает, данные гоняются в память ПК и обратно по медленному пути, и появляются микрофризы. - **Нетграф** (Net graph): Отладочный оверлей, который показывает в игре графики пинга, потерь, FPS и тиков в реальном времени. Если он попал в видео с жалобой на лаги, найти причину намного проще. - **Доля повторных передач** (Retransmission rate): Доля отправленных TCP-пакетов, которые пришлось отправить повторно. Общепринятой нормы нет, но среднее по серверу ниже 0,1% считается здоровым, а выше 1% многие игроки уже легко замечают лаги. Смотрят и на то, во сколько раз значение выросло относительно обычного. - **SACK** (Selective ACK): Функция TCP, с которой получатель подробно сообщает: «этот участок получен, не хватает только вот этого». Даже при нескольких потерях всё восстанавливается за один раз. - **RACK-TLP** (Recent ACK, Tail Loss Probe): Функция TCP, которая определяет потери по времени, а если ACK долго нет, отправляет последний пакет ещё раз, чтобы ускорить восстановление. Включена по умолчанию в современных Linux и Android. В Windows TLP и RACK включены по умолчанию начиная с 10 (1607) и Server 2016, а новый RACK, который восстанавливает и потерянные повторные передачи, есть начиная с Server 2022. Работает только на соединениях с включённым SACK. - **Ложная повторная передача** (Spurious retransmission): Пакет пришёл с опозданием или не по порядку, его сочли потерянным и отправили снова, хотя он не терялся. Линия связи загружается зря, а скорость отправки без нужды снижается. - **Нулевое окно** (Zero window): Буфер получателя заполнен, и он сообщает «пока не присылай». Похоже на повторную передачу, но линия связи в порядке: получающая программа не успевает вовремя читать данные. - **thin stream** (Thin stream): Соединение, по которому, как в играх, изредка отправляются мелкие пакеты. Сигналы для быстрой повторной передачи накапливаются плохо, поэтому при потере соединение надолго замирает. - **Полисер** (Policer): Способ ограничения скорости, при котором пакеты сверх заданной скорости сразу отбрасываются, без постановки в очередь. Способ, при котором пакеты ставятся в очередь и выпускаются медленнее, называется шейпером. - **Пейсинг** (Pacing): Отправка пакетов, равномерно распределённая во времени, без выброса одной пачкой. Не даёт переполнять маленькие буферы. - **ECN** (Explicit Congestion Notification): Функция, при которой перегруженное оборудование, не отбрасывая пакеты, ставит на них отметку «перегрузка», и отправитель снижает скорость. О перегрузке сообщается без потерь. Работает, только если её поддерживают оба конца и оборудование на перегруженном участке. - **MSS** (Maximum Segment Size): Максимальный объём данных в одном TCP-пакете. Обычно 1 460 байт. Если уменьшить его под туннельный участок, можно избежать чёрной дыры MTU. - **Хендовер** (Handover): Переключение телефона в движении с одной базовой станции на другую. - **Перцентиль** (Percentile (p50, p95, p99)): Значение, которое оказывается на заданном проценте позиций, если выстроить все значения по возрастанию. p50 называют медианой, p99 соответствует самому медленному 1 случаю из 100. Показывает всплески, которые прячет среднее. - **Хвостовая задержка** (Tail latency): Длинные задержки, которые случаются изредка, хотя почти всё проходит быстро. В среднем их почти не видно, но именно их игроки запоминают как лаги. - **Синтетический мониторинг** (Synthetic monitoring): Вместо реальных игроков измерительные устройства или серверы в заданных точках периодически отправляют ping, traceroute и т. п. и оценивают качество маршрута. Типичный публичный инструмент: RIPE Atlas. - **Интервал агрегации** (Aggregation interval): Сколько секунд или минут объединено в одну точку графика. Чем длиннее интервал, тем сильнее короткие всплески растворяются в среднем. - **Постмортем** (Postmortem): Разбор после окончания инцидента: что произошло, почему и что изменить. Его пишут ради предотвращения повторов, без поиска виноватых. - **C-state** (CPU idle state): Режим энергосбережения, в который CPU переходит во время простоя. Чем глубже режим, тем больше экономия, но тем дольше выход из него. - **Живая миграция** (Live migration): Облако переносит работающую виртуальную машину на другой хост, например на время обслуживания хоста. В момент переноса возможна короткая остановка. - **SNAT** (Source NAT): NAT, который заменяет адрес источника исходящих пакетов на публичный. Число портов на один публичный адрес ограничено, и когда они заканчиваются, новые соединения не устанавливаются. - **NAT-шлюз** (NAT gateway): Облачный сервис, через который серверы из частной сети выходят в интернет с одного общего публичного адреса. У него есть лимит одновременных соединений к каждому адресату. - **Низкоорбитальный спутниковый интернет** (LEO satellite internet): Интернет через спутники на высоте от сотен до тысяч km. Задержка намного меньше, чем у геостационарных спутников, но при переключении на другой спутник может скакать. - **GeoIP** (IP geolocation): База данных, которая по IP-адресу определяет страну, город и провайдера. В ней бывают неверные или устаревшие записи, из-за которых игрока отправляют на сервер в далёком регионе. - **TLS-сертификат** (TLS certificate): Электронный документ, который подтверждает, что сервер действительно тот, за кого себя выдаёт. У него есть срок действия, и после истечения зашифрованное соединение не устанавливается и войти нельзя. - **Генерация кадров** (Frame generation): Технология, при которой видеокарта вставляет между реально отрисованными кадрами предсказанные и так повышает FPS. Картинка плавнее, но задержка от ввода до экрана может вырасти.