Метрики качества кода под нагрузкой: что ломается первым
Под нагрузкой код редко падает там, где его искали. Сначала растёт очередь, заканчивается пул соединений или появляется скрытый хвост p99. Разбираем метрики, которые показывают причину деградации, а не только факт сбоя.

При росте параллельности с 100 до 300 запросов сервис может ещё показывать нормальный средний ответ, хотя p99 уже ушёл в секунды, очередь задач растёт, а пул соединений к базе работает на пределе. Это и есть главный риск проверки качества кода под нагрузкой: средние значения скрывают первые признаки отказа. Ниже — практический разбор для систем на Prometheus 3.x и OpenTelemetry 1.x: какие метрики смотреть, в каком порядке искать причину и что обычно ломается раньше всего.
Сначала отделите симптом от причины
Код под нагрузкой оценивают не по одной цифре. Ошибки, задержка, пропускная способность и загрузка ресурсов связаны между собой. Если смотреть только на HTTP 500, вы узнаете о проблеме после того, как пользователь уже получил сбой.
Минимальный набор сигналов выглядит так:
- запросы — количество операций в секунду и распределение по endpoint, статусу и типу результата;
- задержка — p50, p95 и p99, отдельно для всего запроса и ключевых внутренних этапов;
- ошибки — доля ответов 5xx, таймаутов, отменённых запросов и ошибок внешних зависимостей;
- насыщение — занятые соединения, длина очередей, CPU, память, диск и лимиты файловых дескрипторов;
- зависимости — время запросов к базе, кэшу, брокеру сообщений и внешним API.
Среднее время ответа полезно для оценки общей картины, но плохо подходит для поиска хвоста. Десять быстрых запросов и один очень медленный могут дать приемлемое среднее значение, хотя именно один из них станет пользовательским таймаутом.
Сноска. Перцентиль — значение, ниже которого укладывается заданная доля наблюдений. p95 означает, что 95% операций быстрее этого значения, а самые медленные 5% находятся выше.
Метрика качества под нагрузкой должна отвечать не только на вопрос «сервис медленный?», но и на вопрос «какой ресурс уже ждёт своей очереди?»
Что ломается первым
1. Очередь и хвост задержки
Первый тревожный сигнал часто появляется не в ошибках, а в разнице между p50 и p99. Если медиана остаётся почти прежней, а p99 растёт, часть запросов уже ждёт освобождения ресурса: соединения с базой, рабочего потока, места в очереди или ответа внешней системы.
Смотрите на задержку в разрезе endpoint и этапов. Общий p99 без разбивки показывает масштаб проблемы, но не объясняет её. Полезнее видеть, сколько времени запрос провёл в обработчике, сколько — в SQL, сколько — в очереди и сколько — в сетевом вызове.
Не добавляйте метки с идентификатором пользователя, URL целиком или случайным request ID. Такая высокая кардинальность раздувает хранилище метрик и сама становится причиной проблем.
2. Пул соединений к базе данных
Приложение может иметь свободный CPU и память, но уже не принимать новые запросы из-за исчерпанного пула соединений. Тогда растёт время ожидания соединения, а не время выполнения SQL. В логах появляются таймауты пула, хотя команда начинает оптимизировать запросы.
Минимально измеряйте:
- размер пула и число занятых соединений;
- время ожидания свободного соединения;
- время выполнения SQL отдельно от времени ожидания;
- число активных транзакций и длительных запросов;
- ошибки подключения и разрывы соединений.
Если пул постоянно заполнен, сначала определите, где задерживаются соединения. Увеличение его размера без проверки лимитов базы может перенести отказ на саму СУБД.
3. Очереди фоновых задач
Для воркеров главная метрика — не только число обработанных задач, а возраст самой старой необработанной задачи. В очередь может поступать столько же сообщений, сколько раньше, но одна медленная операция увеличит время ожидания для всех следующих.
Сопоставляйте скорость поступления и обработки, длину очереди, время выполнения задачи, количество повторных попыток и долю задач, завершившихся ошибкой. Если ретраи включаются без ограничений, одна неисправная зависимость способна создать лавину повторной работы.
4. CPU, память и сборка мусора
Высокая загрузка CPU не всегда означает плохой алгоритм. Это может быть нормальная работа при выросшем трафике. Опаснее ситуация, когда CPU близок к лимиту, p99 растёт, а пропускная способность перестаёт увеличиваться.
Для памяти смотрите не только на общий объём. Нужны скорость аллокаций, паузы сборщика мусора, рост RSS процесса, частота OOM-убийств и объём кэшей. Если память растёт после каждой серии одинаковых запросов и не возвращается, проверяйте удерживаемые ссылки и неограниченные коллекции.
Как связать метрику с конкретным фрагментом кода
Метрики инфраструктуры показывают, что ресурс исчерпан. Трассировка помогает найти место, которое этот ресурс удерживает. Связывайте запрос, лог, метрику и trace ID, но не превращайте идентификаторы в labels метрик.
Для каждого критичного сценария заведите короткую карту пути:
- Вход. Запишите количество запросов, статус и полную задержку endpoint.
- Очередь. Измерьте время до начала обработки, если запрос или задача может ждать воркер.
- База и кэш. Разделите ожидание соединения и фактическое выполнение операции.
- Внешняя система. Отдельно учитывайте DNS, сетевое ожидание, таймаут и ответ зависимости.
- Выход. Сопоставьте успешные ответы, отмены клиентом и ошибки на каждом этапе.
Почему это важно: общая задержка может выглядеть как 800 мс, но внутри окажется 50 мс вычислений, 500 мс ожидания базы и 250 мс внешнего API. Исправлять в таком случае нужно не «медленный код» вообще, а конкретное ожидание.
Метрики, которые стоит сравнивать вместе
| Наблюдение | Что проверить первым | Типичная причина | Риск |
|---|---|---|---|
| p99 растёт, p50 почти не меняется | Очереди и пул соединений | Часть запросов ждёт ресурс | Таймауты у небольшой, но важной доли клиентов |
| CPU растёт вместе с задержкой | Профиль горячего endpoint | Дорогой цикл, сериализация или конкуренция | Пропускная способность перестаёт расти |
| Память растёт, а трафик стабилен | Размеры кэшей и время жизни объектов | Утечка или неограниченное накопление | OOM и перезапуски |
| Ошибки базы растут после увеличения нагрузки | Активные соединения и лимиты СУБД | Исчерпан пул или лимит подключений | Каскадный отказ приложения и базы |
| Очередь растёт при стабильном входящем потоке | Время обработки и ретраи | Воркер медленнее поступления задач | Устаревшие результаты и повторная работа |
Как проводить замеры, чтобы не обмануться
Нагрузочный тест должен фиксировать условия: версию приложения, конфигурацию воркеров, размер базы, объём кэша, тип диска, число экземпляров и профиль трафика. Без этого сравнение «до и после» не доказывает улучшение.
Снимайте минимум три режима:
- рабочая нагрузка — обычное распределение запросов и фоновых задач;
- пиковая нагрузка — ожидаемый максимум с заданной длительностью;
- перегрузка — постепенное увеличение параллельности до момента, когда p99 или ошибки выходят за допустимый предел.
Сравнивайте не только среднее время и максимальный RPS. Зафиксируйте p95 и p99, долю ошибок, очередь, загрузку базы, потребление памяти и число перезапусков. Если изменили один участок, прогоните тот же сценарий на прежнем железе и с теми же данными.
Где метрики вводят в заблуждение
Низкий CPU не означает запас производительности. Сервис может ждать сеть, диск, базу или лимит соединений. В таком случае процессор простаивает, а пользователь ждёт.
Нулевая доля 5xx не означает успех. Приложение способно возвращать HTTP 200 с ошибкой внутри ответа, обрывать соединение или выдавать устаревшие данные из кэша.
Максимальная задержка нестабильна. Один выброс плохо сравнивается между прогонами. Для хвоста используйте p95 и p99, а отдельные экстремальные значения расследуйте по трассам и логам.
Рост пула может ухудшить ситуацию. Больше соединений не равно больше производительности. СУБД, внешний API или лимит файловых дескрипторов могут принять только ограниченное число параллельных операций.
Агрегирование скрывает горячий маршрут. Общая метрика по всему приложению может выглядеть нормально, пока один endpoint не занимает почти все соединения или CPU.
Что настроить после проверки
Начните с одного критичного пользовательского сценария и заведите для него четыре контрольные панели: задержка по перцентилям, ошибки, насыщение ресурсов и зависимости. Затем добавьте правило, которое реагирует на устойчивый рост p99 вместе с очередью или временем ожидания пула, а не на один случайный выброс.
После каждого изменения сохраняйте нагрузочный профиль и сравнение «до и после». В проде отдельно наблюдайте возраст очереди, время ожидания соединений, долю таймаутов и рост памяти. Инвалидация кэша, выбор алгоритма и настройка конкретной базы — отдельные темы; здесь важно сначала увидеть, какой ресурс заканчивается первым.
Аналитик звонков: Разбор записи звонка по этапам сделки: где сорвалось, что упущено, три исправляемые рекомендации с цитатами.
Спросить за 90 ₽Источники: OpenTelemetry: Signals, Prometheus: Metric types
Частые вопросы
Какая метрика важнее: средняя задержка или p99?
Почему сервис медленный, если CPU загружен только на 40%?
Нужно ли увеличивать пул соединений, если он постоянно заполнен?
Что делать, если метрики показывают рост p99, но ошибок нет?
Как понять, что проблема именно в утечке памяти?
Подойдут ли эти метрики для другой версии Prometheus или OpenTelemetry?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.