Если все рабочие потоки заняты медленными задачами, новые запросы ждут неопределённо долго.
Почему Рабочие потоки заняты ожиданием ответа внешних 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)
Опровергает
Если очередь пуста, а запросы всё равно обрабатываются медленно, тормозит сам вызываемый сервис: дело в каскадном отказе или зависимости от внешних сервисов
Чем проверить
Инструменты инфраструктуры (игровой код не нужен)
Подробнее
Если приём пакетов и игровая логика делят один пул рабочих потоков, то, как только несколько медленных задач займут все потоки, обработка пакетов на всём сервере остановится.
Debug ThreadPool StarvationMicrosoft Если в пуле не осталось свободных потоков и новые задачи ждут, ответы замедляются. Причина в блокирующем коде, который занимает потоки. Признак исчерпания в dotnet-counters: CPU намного ниже 100%, а dotnet.thread_pool.thread.count медленно, но постоянно растёт (часто велик и dotnet.thread_pool.queue.length). Где ждут потоки, проверяют через dotnet-stack
Avoiding insurmountable queue backlogsAWS Число одновременно обрабатываемых запросов = интенсивность поступления × задержка (закон Литтла). При 100 запросах в секунду рост задержки со 100 ms до 10 с увеличивает нужное число потоков с 10 до 1 000, и пул исчерпывается
Bulkhead PatternMicrosoft Azure Если для каждого вызываемого сервиса держать отдельный пул соединений и потоков, сбой одного сервиса блокирует только его пул
.NET runtime metrics.NET dotnet.thread_pool.thread.count (число потоков пула) и dotnet.thread_pool.queue.length (число ожидающих задач) доступны начиная с .NET 9
Well-known EventCounters in .NETMicrosoft ThreadPool Thread Count (threadpool-thread-count) и ThreadPool Queue Length (threadpool-queue-length) в .NET 8 и ниже