К основному содержимому
performance⁠-⁠testing.ru

Как читать графики нагрузочного теста: восемь диагнозов

Разбор доклада Алексея Лавренюка на Heisenbug 2017. Симптом на графике и что он означает: от одного загруженного ядра до общей очереди запросов.

analysismetric-latencymethodologygotcha

Самый популярный доклад в этом каталоге записан в 2017 году и набрал 16 тысяч просмотров. Алексей Лавренюк из Яндекса написал специально для доклада сервис на Python Tornado с пятью ручками, каждая со своей поломкой, и последовательно их находит.

Ценность не в инструментах, а в связке «симптом на графике — что он означает». Она не устарела ни на день.

Тайм-коды указывают на запись, любое утверждение проверяется за минуту.

Два графика, на которые смотрят первыми

Слева квантили времён ответа. Чем ниже, тем лучше. Квантиль объясняется без формул: пятидесятый квантиль в 350 мс означает, что в 350 мс уложились половина ответов. Он же медиана. Сотый — максимум, нулевой — минимум [05:00].

Справа распределение времён ответа. Чем выше, тем лучше, потому что высота это число полученных ответов [07:00].

Дальше идут восемь диагнозов, и почти каждый читается по этим двум картинкам.

Диагноз 1. Разрыв между линией и графиком

На графике распределения красная линия показывает, сколько запросов вы отправили, а сам график — сколько ответов получили. Если между ними просвет, сервис перегружен и не успевает отвечать [08:00].

Простейший признак, и его чаще всего пропускают, потому что смотрят только на времена ответа.

Диагноз 2. Полка на ровном числе секунд

Времена ответа выросли и упёрлись в горизонтальную линию на 11 секундах. Это не поведение сервиса, а тайм-аут генератора. Подтверждается графиком сетевых ошибок: там появляются ошибки 110 [19:00].

Полезное следствие: на это состояние вешается автостоп. Тест сам останавливается и записывает достигнутое число как предел производительности [19:00].

Диагноз 3. Процессор упёрся в 3 процента

Машина тридцатидвухъядерная, а загрузка процессора замерла чуть выше трёх процентов. Арифметика простая: 100 делить на 32 — это примерно 3 [21:00].

Значит работает одно ядро из тридцати двух. Проверяется включением помодульного мониторинга: видно, как процесс перескакивает между ядрами, но в каждый момент занято только одно [22:00].

После добавления воркеров процессор загрузился полностью, а число ответов выросло с 20 до 250.

Диагноз 4. Разные времена ответа, одинаковый RPS

Первая ручка отвечает за миллисекунды, вторая за 250 миллисекунд. Логично ожидать, что быстрая отдаёт больше ответов в секунду. На графике обе выдают одинаково [24:00].

Объяснение — лучшая часть доклада, и оно про бар рядом с офисом.

Барменов несколько, но заказы они кладут в общую очередь. Коктейль делается две минуты, виски наливается за десять секунд. В итоге все бармены заняты коктейлями, и человек, заказавший виски, ждёт час.

Это блокировка головы очереди. Лечится разделением: один бармен только на быстрые заказы. В генераторе то же самое — отдельные пулы пользователей и отдельные очереди на каждую ручку [26:00].

После разделения пропускная способность быстрой ручки выросла в разы, а медленная осталась на своих значениях. Что и требовалось: теперь видно каждую по отдельности.

Диагноз 5. Время ответа растёт ровно там, где упёрся процессор

Совпадение по времени и есть диагноз: ручка упирается в вычисления [27:00]. В коде оказался расчёт фракталов.

Тут же наблюдение, которое Лавренюк честно оставляет без объяснения: нагрузка растёт линейно, а потребление процессора — нет. Он говорит, что не выяснял причину, и предлагает залезть в исходники Tornado самостоятельно [28:00].

Это, кстати, потенциальное место для оптимизации: линейный рост CPU вместо нелинейного заметно улучшил бы сервис.

Диагноз 6. Ответы отстают от расписания, а времена не выросли

Выглядит как перегрузка, но это не сервис. Это кончился генератор [31:00].

Проверяется формулой. Среднее время ответа 250 мс, значит один поток даёт 1000/250 = 4 запроса в секунду, а всё вместе — четыре тысячи. Ровно в эту цифру и упёрлись [32:00].

Лавренюк выводит эту формулу прямо в докладе и называет её формулой Литтла. Подробнее про неё — в отдельном разборе.

Диагноз 7. Растёт Connect Time при свободном процессоре

Отдельный график разделяет время установки соединения и время обработки запроса. Если растёт первое, а процессор не загружен, упёрлись в число соединений, а не в вычисления [31:00].

Так вскрылась пятая ручка: пятьдесят инстансов её не пошатнули, потому что она ходит во внешний сервис. Чтобы её продавить, понадобилось три тысячи инстансов и 7500 запросов в секунду [30:00].

Диагноз 8. Медиана растёт плавно, старшие квантили скачут

Признак того, что за одним графиком прячутся ручки с разным поведением [46:00]. Квантильный график показывает распределение внутри каждой секунды: квантили разошлись — большой хвост, сошлись — распределение плотное.

Какую модель когда включать

В докладе это разведено чётко [21:00]:

Задача Модель
Быстро нащупать предел производительности закрытая, растущее число пользователей
Тест для продакшена с автоматикой открытая, жёсткое расписание
Измерить времена ответа для отчёта открытая, постоянная нагрузка после прогрева

Про третий случай сказано отдельно: растущая нагрузка и закрытая модель для этого не годятся, а инстансов надо взять с запасом на случай всплеска [33:00].

Оговорка про точность квантилей

Квантили считаются по посекундным корзинам, поэтому они приблизительные. Точность плавает: на малых временах ответа она доходит до микросекунд, на больших падает до 50 или даже 100 миллисекунд [35:00].

Если нужны точные значения, берётся сырой лог генератора и обрабатывается на Python. Формат простой, набор чисел в человекочитаемом виде.

Что устарело, а что нет

Устарел инструментарий вокруг. Phantom, на котором построены примеры, давно вытеснен Pandora. Overload как сервис хранения отчётов остался в прошлом.

Не устарело ничего из перечисленного выше. Разрыв между отправленными и полученными, полка на тайм-ауте генератора, арифметика «сто делить на число ядер», общая очередь — всё это встречается ровно так же в k6, Gatling и JMeter.

Позиция автора, с которой стоит спорить

На вопрос из зала, зачем нагрузочное тестирование, если есть выкатка на часть продакшена, Лавренюк отвечает так [41:00]:

Прод даёт ответ на вопрос «летит или не летит». Нагрузочное тестирование — это осциллограф для разработчика, который показывает узкие места.

И дальше про метод: систему бьют на компоненты, каждый прогоняют отдельно, и знание собирается по кубикам. Он сам признаёт слабое место: корректного масштабирования результата с компонента на всю систему он ни разу не видел.

Закрывает доклад ссылкой на статью с тезисом, что все нагрузочные инструменты лгут, и советом: чтобы с ними совладать, надо в них разбираться [39:00].

Источники

  1. Алексей Лавренюк, «Учимся анализировать результаты нагрузочного тестирования» — Heisenbug, 4 октября 2017. Тайм-коды ниже указывают на эту запись(YouTube)

Доклады по теме