Нефункциональное тестирование и нагрузка
Я снял 2000 запросов к сервису и посчитал. Среднее время ответа - 8,4 миллисекунды, звучит отлично. Но половина запросов уложилась в 1,9 миллисекунды, а самый медленный процент шёл по 44, и один запрос из тысячи - по 253. Медленнее среднего оказалось всего 13% запросов: среднее не описывает вообще никого.
Стержень: скорость меряют перцентилями, а любой нефункциональный тест начинается с измеримой цели, иначе «прогнали, вроде держит» ничего не значит.
// Формулировки: «какие бывают виды нагрузочного тестирования?», «почему p99, а не среднее?», «как QA проверяет безопасность?».
Замер: почему среднее врёт
Разберём те цифры. Перцентиль - это порог, ниже которого лежит заданная доля значений: p99 равен 44 миллисекундам значит, что 99% запросов ответили быстрее 44, а оставшийся процент - хуже. Мои измерения: p50 (он же медиана - ровно половина запросов быстрее) 1,9 мс, p90 42 мс, p95 42,5 мс, p99 43,7 мс, p99,9 253 мс. Среднее - 8,4 мс - не совпадает ни с чьим опытом: оно хуже, чем у 87% запросов, и радикально лучше, чем у оставшихся 13%.
Откуда берётся такой хвост, я знаю точно, потому что сам его и построил: большинство запросов лёгкие, но часть тяжёлые (у клиента много данных), а изредка случается пауза сборщика мусора - GC (garbage collector), механизм, который освобождает память и на это время подмораживает обработку. В живых системах хвост растят те же причины плюс очередь: пока сервер занят тяжёлым запросом, лёгкие ждут.
// И почему хвост важнее, чем кажется. Один процент - это мало, пока не посчитаешь. За сеанс пользователь делает десятки запросов: при 30 запросах вероятность хотя бы раз попасть в худший процент - 26%, при 100 запросах - 63%. То есть «всего 1% медленных» на деле означает, что каждый четвёртый сеанс притормозил. А при 200 000 запросов в сутки хуже p99 идут 2000 штук - это 2000 поводов написать в поддержку.
- перцентиль
- порог, ниже которого лежит заданная доля измерений
- хвост распределения
- малая доля самых медленных запросов, где и живут жалобы
Виды нагрузки и цель до запуска
Нагрузочное тестирование - это семейство, и каждый вид отвечает на свой вопрос. Load: держим ли обещанное качество на плановой нагрузке. Stress: где именно система ломается, если давить сверх плана, и как она при этом ломается - деградирует плавно или падает целиком. Soak (он же endurance): что будет, если держать нагрузку часами - вылезают утечки памяти, когда занятая память не возвращается системе и потихоньку растёт, и переполнение очередей. Spike: как переживается резкий всплеск, скажем, рассылка на всю базу.
Прогон без заранее заданной цели бессмысленен, потому что не с чем сравнивать результат. Цель формулируют цифрами: 95% запросов быстрее 300 миллисекунд при 500 запросах в секунду. Такие обещания пишут в SLA (service level agreement) - соглашение об уровне сервиса с клиентом - или в SLO (service level objective), внутреннюю цель команды. Нагрузку меряют в RPS (requests per second), запросах в секунду.
// Профиль нагрузки берут из статистики прода - боевой среды, где сидят живые пользователи, - а не придумывают. Если в жизни 80% запросов - открытие списка и 5% - оформление заказа, а тест гонит поровну, вы измерили несуществующую систему. Аккуратность тут дешевле переделки: неверный профиль даёт красивые цифры, которые ни о чём не говорят.
- load / stress / soak / spike
- плановая нагрузка / за пределом / длительная / резкий всплеск
- SLO
- service level objective - внутренняя цель по скорости и доступности
Безопасность руками тестировщика
Я поднял маленький сервис с заказами и зашёл под пользователем номер 7. Запросил свой заказ - вернулся мой, на 1500 рублей. Потом поменял в адресе цифру: запросил заказ номер 2. Сервер ответил 200 и отдал чужой заказ на 999 999 рублей. Никакой хитрости, одна изменённая цифра в адресной строке.
Это IDOR (insecure direct object reference) - доступ к чужому объекту простой подменой идентификатора. Классика, которую находит именно тестировщик, потому что разработчик проверяет свой сценарий под своей учёткой. Правило: авторизацию проверяют между ролями и между пользователями, а не под собой. Рядом живут остальные пункты списка OWASP (Open Web Application Security Project) Top 10 - перечня самых частых видов уязвимостей. Инъекция: текст, введённый пользователем, исполняется как часть запроса к базе, и кавычка в поле поиска вытаскивает чужие строки. XSS (cross-site scripting): чужой скрипт исполняется в браузере вашего пользователя и уносит его сессию. Сломанная проверка входа: под чужой учёткой можно оказаться без пароля.
// Соседняя ось - доступность. Проверяется руками: пройти сценарий одной клавиатурой без мыши, включить экранный диктор, померить контраст текста. Требования к этому сведены в WCAG (web content accessibility guidelines) - международный набор рекомендаций по доступности веб-содержимого.
- IDOR
- insecure direct object reference: чужой объект по подменённому идентификатору
- OWASP Top 10
- список самых частых классов веб-уязвимостей, годится как чек-лист
Как отвечать: «Почему смотрят p95/p99, а не среднюю латентность?»
Потому что средняя латентность - то есть среднее время ответа - прячет именно тех, кому плохо. Я как-то снимал 2000 запросов к сервису: среднее вышло 8,4 миллисекунды, а по факту половина запросов отвечала за 1,9, худший процент шёл по 44, и один запрос из тысячи - по 253 миллисекунды. Медленнее среднего оказалось всего 13% запросов, то есть цифра не описывала ни быстрых, ни медленных. Перцентиль говорит прямо: p99 равен 44 - значит, 99% запросов быстрее, а один процент хуже. И этот процент важнее, чем кажется, по двум причинам. Первая: за сеанс пользователь делает десятки запросов, при тридцати вероятность хотя бы раз попасть в худший процент уже 26%, при сотне - 63%, то есть тормозит каждый третий-четвёртый сеанс. Вторая: на потоке в 200 тысяч запросов в сутки один процент - это две тысячи медленных ответов, то есть живые обращения в поддержку. Поэтому цели по скорости формулируют в перцентилях: 95% запросов быстрее трёхсот миллисекунд при такой-то нагрузке. Среднее годится разве что для грубого тренда.
Почему это сильный ответ: показано на измерениях, что среднее не описывает никого, объяснён смысл перцентиля, посчитано накопление по сеансу и по суточному потоку, и сделан вывод, что цели пишут в перцентилях.
На чём валят
- −Отчитываться средним временем ответа: медленнее среднего было лишь 13% запросов, а p99,9 хуже него в 30 раз.
- −Гонять нагрузку без цели в цифрах - «вроде держит» не сравнить ни с чем.
- −Придумывать профиль нагрузки из головы вместо статистики прода - измеряется несуществующая система.
- −Проверять доступ только под своей ролью: одна изменённая цифра в адресе отдала чужой заказ.
- −Считать нагрузочный тест разовым мероприятием перед релизом, а не регулярным замером.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 14, остальные разбираются в тренажёре.
- Чем нагрузочное (load) тестирование отличается от стрессового (stress)?A)Load проверяет безопасность приложения, а stress — скорость работы его интерфейсаB)Это синонимы: оба подают на систему одну и ту же нагрузкуC)load проверяет работу под ожидаемой нагрузкой, stress — за её пределами, до отказаD)Load запускают только вручную, а stress — исключительно через инструменты
показать ответ и разбор
+C)load проверяет работу под ожидаемой нагрузкой, stress — за её пределами, до отказа// разбор: Load-тест подаёт ожидаемую (целевую) нагрузку — проверяет, что система держит штатный и пиковый трафик с приемлемым временем отклика. Stress-тест сознательно уходит за пределы: наращивает нагрузку до отказа, чтобы найти точку деградации и увидеть, как система падает и восстанавливается (плавно или полным крахом). Цель разная: load — «тянет ли норму», stress — «где и как ломается». Оба — подвиды нагрузочного тестирования.
- Чем время отклика (response time) отличается от пропускной способности (throughput)?A)Это одно и то же, просто выраженное в разных единицах измеренияB)Отклик — сколько ждёт один запрос, throughput — сколько запросов система тянет за секундуC)Отклик измеряют для API, а throughput — только для баз данныхD)Отклик целиком зависит от сети, а throughput — исключительно от кода приложения
показать ответ и разбор
+B)Отклик — сколько ждёт один запрос, throughput — сколько запросов система тянет за секунду// разбор: Время отклика (латентность) — сколько ждёт один запрос от отправки до ответа (в мс). Пропускная способность (throughput) — сколько запросов система обрабатывает за единицу времени (RPS). Это разные оси: система может держать высокий throughput при большой латентности (за счёт очереди) и наоборот. Обе метрики нужны вместе: быстрый отклик при низком throughput — узкое место по параллелизму. Тестировщик смотрит их в связке с ростом нагрузки.
- Почему производительность оценивают по перцентилям (p95/p99), а не по среднему времени отклика?A)Перцентили технически считать проще и быстрее, чем среднее арифметическое по выборкеB)Среднее время отклика не получается посчитать по такой выборкеC)Перцентили измеряют пропускную способность, а среднее — только латентностьD)Среднее прячет длинный хвост медленных запросов
показать ответ и разбор
+D)Среднее прячет длинный хвост медленных запросов// разбор: Среднее сглаживает выбросы: при среднем 200 мс каждый двадцатый запрос может занимать 3 секунды, и именно эти пользователи уходят. p95 означает «95% запросов быстрее X», p99 ловит совсем хвост. SLA и цели производительности задают в перцентилях, потому что важен опыт худших случаев, а не «в среднем нормально». Тестировщик смотрит рост p95/p99 под нагрузкой, а не одну усреднённую цифру.
- Что ловит soak (endurance) тестирование — долгий прогон под умеренной нагрузкой?A)Проблемы, копящиеся со временем: утечки памяти, рост очередей, переполнение логов и дискаB)Максимальный кратковременный пик нагрузки, который система выдержит за одну минутуC)Корректность бизнес-логики на граничных значениях входных данныхD)Соответствие вёрстки макету на разных разрешениях экрана
показать ответ и разбор
+A)Проблемы, копящиеся со временем: утечки памяти, рост очередей, переполнение логов и диска// разбор: Soak (endurance) — прогон под штатной нагрузкой, но долго (часы, сутки). Ловит дефекты, невидимые в коротком тесте: утечки памяти, постепенный рост числа открытых соединений, разбухание логов и диска, деградацию из-за фрагментации или неочищаемых кэшей. Система, идеально проходящая 10-минутный load, может лечь через 8 часов. Поэтому endurance-прогон обязателен перед выводом в прод сервисов с долгим аптаймом.
- Зачем в нагрузочном тесте задают профиль с плавным ростом (ramp-up), а не бьют сразу пиком?A)Плавный рост нагрузки заметно ускоряет тест и экономит общее время прогонаB)Иначе инструмент нагрузки не сможет сгенерировать достаточно много запросовC)Чтобы увидеть, при какой нагрузке начинается деградация, и отделить прогрев от отказаD)Плавный рост нужен только для UI-тестов, но не для нагрузочных
показать ответ и разбор
+C)Чтобы увидеть, при какой нагрузке начинается деградация, и отделить прогрев от отказа// разбор: Ramp-up постепенно наращивает виртуальных пользователей и показывает, где именно начинается деградация: до какой нагрузки отклик стабилен и на каком пороге ломается. Мгновенный пик даёт бинарный ответ «упало/не упало» и смешивает эффект холодного старта (прогрев кэшей, пулов, JIT) с реальным поведением под нагрузкой. Плавный профиль ближе к реальному трафику и даёт кривую «нагрузка → отклик», по которой ищут узкое место.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.