Нагрузочное тестирование приложения интернет-магазина перед распродажей: что проверить
Распродажа даёт интернет-магазину пик трафика, которого приложение не видит в обычный день ни разу. Нагрузка приходит неравномерно: первые минуты после push-рассылки или старта акции создают заметно более плотный поток запросов, чем следующий час. Разберём, какие сценарии приложения проверяют до пика, как построить нагрузочную модель и по каким критериям приёмки принимают решение, что приложение к распродаже готово.
Что такое нагрузочное тестирование приложения и что оно показывает бизнесу
Нагрузочное тестирование приложения — контролируемое воспроизведение ожидаемого потока покупателей на тестовой среде с замером времени ответа, пропускной способности и доли ошибок. Инженер задаёт профиль: сколько человек заходит в приложение, что они делают и с какими паузами. Система отвечает цифрами, по которым видно точку замедления.
Бизнесу тест отвечает на два вопроса: сколько одновременных покупателей приложение обслуживает без потерянных заказов и какой узел замедляется первым — поиск по каталогу, пересчёт корзины или создание заказа. Второй ответ ценнее: он показывает, куда вкладывать время до распродажи.
Отличие от функциональной проверки
Функциональное тестирование и QA отвечают на вопрос, работает ли сценарий. Нагрузочная — работает ли тот же сценарий, когда его одновременно проходят тысячи покупателей. Оформление заказа бывает безупречным на одном устройстве и терять часть заказов на пике из-за таймаута к платёжному шлюзу.
Расположение узкого места в мобильном приложении
Приложение интернет-магазина работает как тонкий клиент: витрина, корзина и оформление ходят за данными в API. Основная нагрузка приходится на серверную часть и базу данных, поэтому проверяют в первую очередь API. Клиентскую часть измеряют отдельно и на устройствах: время холодного старта, отрисовку длинных списков, кэширование изображений, долю сессий без падений.
Пять сценариев, которые проверяют перед распродажей
Покупатель открывает каталог десятки раз, корзину — несколько раз, оплату — один раз за сессию. Прогон повторяет эти пропорции, но каждый сценарий разбирают отдельно: отказывают они по разным причинам.
Каталог и поиск
Каталог даёт наибольшее число запросов за сессию. Под нагрузкой здесь выходят наружу тяжёлые выборки из базы: фильтры по нескольким свойствам, сортировка по цене со скидкой, подсчёт числа найденных товаров. Отдельная поломка — одновременное истечение кэша популярной категории: сотни запросов уходят в базу в одну секунду, и она становится узким местом. Проверяют листинг категории, фильтры, поиск по строке, карточку товара и блок рекомендаций. Картинки меряют отдельно: их отдаёт сеть доставки контента.
Корзина и расчёт скидок
Корзина на распродаже пересчитывается чаще обычного: акционные цены, промокоды, пороги бесплатной доставки, подарки за сумму заказа. Каждое добавление товара запускает перерасчёт всей корзины, и стоимость операции растёт вместе со сложностью правил акции. Проверяют добавление и удаление товара, изменение количества, промокод и параллельные записи в одну корзину с двух устройств. Контрольная цифра — число потерянных записей: после прогона состав корзины должен совпадать с последовательностью действий.
Авторизация и обновление сессий
Вход по коду из SMS или по push-уведомлению зависит от внешнего поставщика с собственным лимитом сообщений в секунду. На пике лимит выбирается первым, и покупатель видит ошибку входа при полностью работоспособном приложении. Вторая поломка — одновременное обновление токенов доступа: после массовой рассылки или принудительной инвалидизации сроки жизни токенов выравниваются, и клиенты приходят за обновлением одной волной. В приложении волну разводят случайным разбросом момента обновления. Проверяют регистрацию, вход по коду, обновление токена и ограничитель частоты запросов.
Оплата
Последствия ошибки в оплате обходятся дороже, чем в остальных сценариях: магазин теряет заказ или покупатель получает двойное списание. Под нагрузкой проверяют создание платежа, повторную отправку запроса после таймаута и приём уведомлений от платёжного шлюза. Ключевое свойство — идемпотентность: повторный запрос с тем же ключом не создаёт второй платёж. Боевой контур платёжного поставщика нагружать нельзя: работают с его тестовым контуром и заявленными лимитами по числу операций.
Остатки и резервирование товара
Дефицитная позиция на распродаже — источник продаж сверх остатка. Несколько покупателей одновременно оформляют последние единицы, и без блокировки на резерве магазин принимает заказы, которые нечем закрыть. Второй источник расхождений — обмен с учётной системой: очередь обновлений накапливается, и витрина показывает устаревший остаток. Проверяют резервирование под одновременными заказами, отмену резерва, глубину очереди обмена и расхождение остатков до и после прогона.
Нагрузочная модель: от плана распродажи к профилю запросов
Нагрузочная модель переводит план распродажи в язык запросов в секунду. Исходных данных нужно немного: план по заказам, доля приложения в заказах, план коммуникаций и данные прошлого пика из мониторинга. Без модели прогон превращается в произвольное число виртуальных пользователей, по которому нельзя принять решение.
Состав модели
Модель описывает четыре вещи:
- Смесь сценариев. Доли покупателей, которые смотрят каталог, доходят до корзины и оформляют заказ.
- Паузы между действиями. Человек читает карточку товара несколько секунд, а не отправляет запросы подряд.
- Форму нагрузки. Скорость выхода на целевое значение, длительность плато и спад.
- Целевое и предельное значения. Нагрузка, которую нужно держать, и нагрузка, на которой проверяют поведение системы при перегрузе.
Пример расчёта
Приведём условный пример расчёта — это арифметика по заданным допущениям, а не результаты измерений конкретного проекта. Цифры исходных допущений подставляют из планов магазина.
Условный пример пересчёта плана распродажи в целевую нагрузку на API
| Шаг расчёта | Как получено | Значение |
|---|---|---|
| План заказов за сутки распродажи | допущение из плана магазина | 20 000 заказов |
| Доля заказов из приложения | допущение: 60 % | 12 000 заказов |
| Заказы в пиковый час | допущение: 25 % заказов из приложения | 3 000 заказов в час |
| Сессии в пиковый час | 3 000 ÷ допущение: доля сессий с заказом 4 % | 75 000 сессий |
| Сессий в секунду | 75 000 ÷ 3 600 секунд | ≈ 21 сессия в секунду |
| Запросов к API на сессию | допущение по данным мониторинга: каталог 10 + карточки 4 + корзина 3 + оформление 1 | 18 запросов |
| Нагрузка в пике | 20,8 × 18 | ≈ 375 запросов в секунду |
| Целевое значение теста с запасом | 375 × 1,5 (повторы и фоновые запросы) | ≈ 560 запросов в секунду |
| Одновременные сессии | 20,8 в секунду × 360 секунд (длительность сессии из мониторинга) | ≈ 7 500 сессий |
| Предельное значение для стресс-прогона | 560 × 2 | ≈ 1 120 запросов в секунду |
Из расчёта получается проверяемое задание: держать 560 запросов в секунду в течение часа при 7 500 одновременных сессиях. Множитель 1,5 закрывает повторные отправки после таймаутов, обновление токенов и фоновые запросы. Его подбирают по данным прошлого пика, а при их отсутствии фиксируют в отчёте как явное допущение.
Виды прогонов: от проверочного до поиска предела
Один прогон не закрывает подготовку: типы тестов отвечают на разные вопросы. Классификация ниже совпадает с той, которую описывает документация инструмента k6 — она устоялась в индустрии и удобна для планирования.
Типы прогонов и вопросы, на которые они отвечают. Длительности — типовые значения, их уточняют под проект
| Тип прогона | Какой вопрос закрывает | Профиль и длительность |
|---|---|---|
| Проверочный (smoke) | Корректен ли сам сценарий теста и его данные | Единицы пользователей, 5–10 минут |
| Базовый на целевом значении | Держит ли система запланированную нагрузку распродажи | Целевое значение, 30–60 минут |
| Пиковый (spike) | Как система переживает мгновенный приток после рассылки | Резкий заход выше целевого значения за 1–2 минуты, короткое плато и спад |
| Стресс-прогон | Что происходит при нагрузке выше плановой | Двойное целевое значение, 20–30 минут |
| Поиск предела (breakpoint) | На каком значении система отказывает и чем именно | Ступенчатый рост до отказа |
| Прогон на выносливость (soak) | Копятся ли утечки памяти, соединений и места на диске | 70 % целевого значения, 4 часа |
Поиск предела отвечает ещё на один вопрос — каким способом система отказывает. Управляемая деградация и каскадный отказ всех сервисов различаются по последствиям для выручки, хотя на графике оба выглядят как превышение порога.
Метрики: p95, доля ошибок API, пропускная способность
Среднее время ответа скрывает то, из-за чего покупатель уходит. 94 запроса по 90 мс и 6 запросов по 3 секунды дают среднее около 265 мс — в пределах порога, тогда как p95 равен 3 секундам. Решения принимают по процентилям.
- p95 времени ответа. Порог, в который укладываются 95 запросов из 100; у остальных пяти ответ приходит позже. Основная метрика приёмки для каталога и корзины.
- p99 времени ответа. Тот же показатель для 99 запросов из 100. Нужен для оплаты и создания заказа, где важен хвост распределения.
- Доля ошибок API. Считается раздельно: ответы 5xx (отказ сервера), ответы 4xx (отклонённый запрос, в том числе по лимиту) и таймауты. Бизнес-отказы вида «товар закончился» в эту долю не входят.
- Пропускная способность. Число обработанных запросов в секунду и число завершённых заказов в минуту. Второе важнее: оно показывает, доходят ли покупатели до конца.
- Насыщение ресурсов. Загрузка процессора, память, занятость пула соединений к базе, длина очередей, доля попаданий в кэш — по этим цифрам находят причину замедления.
- Клиентские метрики. Время до первого экрана, холодный старт, доля сессий без падений. Измеряются на устройствах, а не генератором нагрузки.
Для серверной части метрики снимает система мониторинга, для базы — её собственная статистика: у PostgreSQL это pg_stat_statements по самым дорогим запросам и pg_stat_activity по ожиданиям и блокировкам. Отдельно предел самой базы измеряют синтетическим профилем — для этого есть штатный pgbench. Клиентские показатели снимают средствами мониторинга приложения на устройствах.
Критерии приёмки перед распродажей
Критерии приёмки превращают прогон в решение. Без согласованных порогов обсуждение результата сводится к спору о том, «приемлемо» ли время ответа. Пороги фиксируют до первого прогона и согласуют с владельцем продукта.
Пример таблицы критериев приёмки. Пороги калибруют под конкретный проект по данным прошлого пика
| Сценарий | Метрика | Порог на целевой нагрузке | Действие при превышении |
|---|---|---|---|
| Листинг категории | p95 ответа API, доля 5xx | ≤ 600 мс, ≤ 0,1 % | Кэширование выдачи, пересмотр выборок и индексов |
| Поиск по каталогу | p95 ответа API | ≤ 800 мс | Вынос поиска в отдельный индекс, ограничение глубины страниц |
| Карточка товара | p95 ответа API | ≤ 500 мс | Отключение необязательных блоков под пиком |
| Добавление в корзину | p95 ответа API, потерянные записи | ≤ 700 мс, 0 записей | Упрощение перерасчёта акций, фиксация правил на время распродажи |
| Вход по коду | p95 на нашей стороне до вызова поставщика, доля отказов | ≤ 600 мс, ≤ 0,5 % | Повышение лимита у поставщика, второй канал доставки кода. Задержку самого поставщика берут из его условий обслуживания, а не из прогона |
| Создание заказа | p99 ответа API, доля ошибок, двойные заказы | ≤ 2 000 мс, ≤ 0,2 %, 0 заказов | Асинхронное оформление с очередью, повторная проверка идемпотентности |
| Инициализация оплаты | p99 ответа, доля таймаутов, двойные списания | ≤ 2 500 мс, ≤ 0,3 %, 0 списаний | Повтор с тем же ключом, резервный платёжный способ |
| Остатки и резерв | Расхождение остатков, продажи сверх остатка | 0 позиций, 0 заказов | Блокировка на резерве, учёт дефицитных позиций отдельной логикой |
| Система на целевом профиле | Доля ошибок, деградация ответа к проверочному прогону | ≤ 0,5 %, не более чем в 2 раза | Горизонтальное расширение, ограничение частоты запросов на входе |
| Прогон на выносливость | Рост потребления памяти за 4 часа | ≤ 10 % | Поиск утечки, перезапуск по расписанию как временная мера |
Колонка с действием важна не меньше порога. Она превращает отчёт в план работ и заранее отвечает на вопрос, что команда сделает за оставшиеся дни, если порог не выдержан.
Среда, данные и ограничения внешних систем
Результат прогона имеет смысл, только если среда сопоставима с боевой. Тестовый контур на одной машине с базой из тысячи товаров даёт цифры, которые нельзя переносить на распродажу напрямую. Сопоставимости добиваются двумя путями: повторяют конфигурацию боевой среды либо уменьшают её кратно по числу узлов и фиксируют в отчёте, что пересчёт применим к пропускной способности, но не к задержкам на блокировках и пуле соединений.
Требования к тестовым данным
Каталог нужен полного объёма, с тем же распределением популярности товаров: если все виртуальные покупатели ищут один товар, кэш отвечает на каждый запрос и прогон показывает завышенный результат. Учётные записи для теста создают обезличенными — это помогает учитывать требования 152-ФЗ к обработке персональных данных. Платежи проводят в тестовом контуре поставщика, картами из его документации.
Границы внешних систем и размещение генератора нагрузки
Внешние системы задают границы, которые не зависят от команды магазина: лимит сообщений у поставщика SMS, число операций в секунду у платёжного шлюза, квоты сервиса рекомендаций. Эти лимиты узнают заранее и закладывают в модель как потолок. Сам генератор нагрузки размещают за пределами контура приложения, иначе он конкурирует с ним за процессор и сеть, и измеряется уже не приложение.
Мониторинг и выбор инструмента
Мониторинг и трассировку включают до первого прогона: без них результат сводится к факту «порог превышен» без указания места, а по трассировке видно, какой вызов занял время. Инструмент подбирают под команду — Apache JMeter описывает сценарии без программирования, Locust задаёт их кодом на Python и ближе команде с серверной частью на Python и Django.
План отката и переключатели функций
Мобильное приложение откатить так же, как серверную часть, невозможно: версия, уже установленная на устройстве покупателя, остаётся у него, а стор не принимает публикацию с пониженным номером версии — откат оформляется только как новая версия, и она проходит модерацию. Остановить поэтапную раскатку можно быстро, но на тех, кто уже обновился, это не действует. Поэтому обратимость решений обеспечивают на стороне сервера, и план отката готовят заранее.
- Переключатели функций (feature flags). Управляемые с сервера признаки, которые включают и отключают отдельные возможности без выпуска новой версии приложения. Под пиком отключают необязательное: персональные подборки, отзывы, расширенные фильтры.
- Управляемая деградация. Витрина отвечает из кэша, оформление заказа встаёт в очередь, тяжёлая аналитика откладывается. Покупатель видит медленный магазин, а не ошибку.
- Готовая предыдущая версия серверной части. Развёрнутая и прогретая, с проверенной обратной совместимостью по API: старое приложение на устройстве покупателя должно продолжить работать.
- Условия и ответственный. Пороги, при которых решение об откате принимается, и роль, которая его принимает, — вместе с контрольным временем на выполнение.
План отката проверяют тем же прогоном: переключают признаки под целевой нагрузкой и смотрят, переживает ли приложение переключение без ошибок. Под нагрузкой откат ведёт себя иначе, чем на спокойной среде, — чаще всего из-за кэшей, которые после переключения заполняются заново. Для серверной части на Django кэш-слой настраивают по штатной документации фреймворка.
Типичные ошибки нагрузочного тестирования
Ошибки ниже повторяются от проекта к проекту и обесценивают прогон: формально тест пройден, а на распродаже приложение ведёт себя иначе.
- Один сценарий вместо смеси. Прогон только по каталогу нагружает чтение и оставляет запись в базу без проверки, хотя на распродаже чтение и запись идут одновременно.
- Тест по прогретому кэшу. Виртуальные покупатели запрашивают одни и те же данные, кэш отвечает мгновенно, и база в прогоне почти не участвует.
- Отсутствие паузы между действиями. Запросы подряд создают нагрузку, которой не бывает у людей, и искажают число одновременных сессий.
- Генератор внутри того же контура. Инструмент конкурирует с приложением за процессор и сеть, и измеряется уже не приложение, а их сумма.
- Решение по среднему времени ответа. Среднее прячет медленный хвост запросов, по которому покупатели и уходят из приложения.
- Мониторинг только со стороны генератора. Видно, что ответ медленный, но не видно где: в базе, в кэше или во внешнем вызове.
- Игнорирование лимитов внешних систем. Поставщик SMS или платёжный шлюз упираются в квоту раньше приложения, и порог превышается не по вине магазина.
- Прогон за день до распродажи. Находки остаются без времени на исправление и повторную проверку, и отчёт превращается в констатацию.
Чек-лист: что проверить за две недели до распродажи
Две недели — минимальный разумный горизонт: остаётся время на исправления и повторный прогон. Отсчёт идёт от дня старта распродажи.
За 14–10 дней. Согласовать нагрузочную модель и критерии приёмки с владельцем продукта. Поднять тестовую среду, сопоставимую с боевой, и наполнить её каталогом полного объёма. Узнать лимиты внешних систем. Включить мониторинг и трассировку. Провести проверочный прогон на единицах пользователей.
За 9–5 дней. Выполнить базовый прогон на целевом значении и пиковый прогон с быстрым выходом на нагрузку. Снять метрики по каждому из пяти сценариев. Провести стресс-прогон и поиск предела, зафиксировать способ отказа. Собрать список находок с оценкой трудоёмкости.
За 4–2 дня. Закрыть находки, которые влияют на пороги приёмки. Выполнить повторный прогон на целевом значении и прогон на выносливость. Проверить переключатели функций и откат под нагрузкой.
За 1 день. Заморозить изменения в приложении и серверной части. Назначить дежурную смену, согласовать условия отката и ответственного за решение.
День распродажи. Держать открытыми графики времени ответа, доли ошибок и числа завершённых заказов. Сверять фактическую нагрузку с модельной: превышение целевого значения теста — повод отключить необязательные функции заранее, не дожидаясь превышения порогов.
Итог
Выигрышный порядок подготовки к распродаже — сначала нагрузочная модель и критерии приёмки, затем прогоны по сценариям каталога, корзины, авторизации, оплаты и остатков, и только после них правки. Решения принимаются по p95 и p99 времени ответа и по доле ошибок API, а не по среднему значению. Отдельной проверки требует план отката: у мобильного приложения обратимость живёт на стороне сервера, в переключателях функций и в управляемой деградации.
Числа в примере расчёта и пороги в таблице критериев приёмки — иллюстрация метода, а не результаты измерений конкретного проекта: их калибруют по данным прошлого пика и по плану текущей распродажи. Документация инструментов и фреймворков указана ссылками в соответствующих разделах.
Если нужно спланировать нагрузочные прогоны под конкретную распродажу, обсудите задачу с нашей командой: разберём профиль нагрузки, пороги приёмки по сценариям и план отката под ваш проект. Для устранения найденных проблем и сопровождения приложения во время акции можно подключить техническую поддержку FITTIN.
Частые вопросы
Как рассчитать нагрузку на приложение интернет-магазина перед распродажей?
Расчёт идёт от плана по заказам. Из суточного плана выделяют долю приложения, затем долю заказов, которая приходится на пиковый час. Число заказов делят на долю сессий с заказом и получают сессии в пиковый час, а их — на 3 600 секунд. Полученное число сессий в секунду умножают на среднее количество запросов к API за сессию и добавляют запас на повторные отправки и фоновые запросы. Результат — целевое число запросов в секунду, которое держат в тесте.
Что такое p95 и почему его считают вместо среднего времени ответа?
p95 — порог времени ответа, в который укладываются 95 запросов из 100; у остальных пяти ответ приходит позже. Среднее значение скрывает медленный хвост: 94 ответа по 90 мс и 6 ответов по 3 секунды дают среднее около 265 мс, тогда как p95 равен 3 секундам. По процентилям видно именно то, с чем сталкивается медленная часть аудитории. Для оплаты и создания заказа дополнительно считают p99, где хвост распределения влияет на выручку сильнее всего.
Чем нагрузочное тестирование отличается от функционального?
Функциональная проверка отвечает на вопрос, работает ли сценарий по заданным правилам. Нагрузочная отвечает на вопрос, работает ли тот же сценарий при запланированном числе одновременных покупателей и в какой точке он начинает замедляться. Эти проверки не заменяют друг друга: оформление заказа бывает корректным на одном устройстве и терять часть заказов на пике из-за таймаута к платёжному шлюзу или исчерпанного пула соединений к базе данных.
Какие инструменты используют для нагрузочного тестирования API?
Выбор зависит от того, кто пишет сценарии. Apache JMeter описывает их в графическом интерфейсе и подходит команде тестирования без навыков программирования. k6 и Locust задают сценарии кодом — на JavaScript и на Python соответственно, что удобно, когда прогоны запускаются автоматически вместе с выпуском версий. Собственную статистику базы снимают её расширениями — pg_stat_statements и pg_stat_activity у PostgreSQL; pgbench используют отдельно, чтобы измерить предел самой базы синтетическим профилем. Инструмент при этом второстепенен: решает корректность модели и наличие мониторинга на стороне приложения.
Можно ли проводить нагрузочное тестирование на боевом сервере?
Прогон на боевой среде даёт самые достоверные цифры, но проводят его в окно низкого трафика, с предупреждением всех причастных команд и с готовым планом остановки. Данные для такого прогона используют тестовые, чтобы не создавать настоящие заказы и списания. Боевые контуры внешних поставщиков — платёжного шлюза, сервиса SMS — нагружать нельзя: для них берут тестовый контур и заявленные лимиты. Если окна для прогона нет, работают на сопоставимой тестовой среде и фиксируют коэффициент пересчёта в отчёте.
За сколько времени до распродажи начинать нагрузочное тестирование?
Разумный минимум — две недели до старта акции. Первые пять дней уходят на согласование модели и критериев приёмки, подготовку среды и данных, включение мониторинга. Следующие пять — на базовый, пиковый и стресс-прогоны со сбором метрик по каждому сценарию. Следующие три дня уходят на исправления, повторный прогон и проверку отката, а последний день — на заморозку изменений и дежурство. Прогон за день до распродажи оставляет находки без времени на исправление, поэтому его результат мало на что влияет.
Как откатить изменения в мобильном приложении во время распродажи?
Версия, установленная на устройстве покупателя, остаётся у него, а стор не принимает публикацию с пониженным номером версии: откат оформляется только как новая версия, и она проходит модерацию. Остановка поэтапной раскатки действует лишь на тех, кто ещё не обновился. Поэтому обратимость закладывают на стороне сервера. Рабочая схема — переключатели функций, которыми отдельные возможности отключают без выпуска версии, готовая предыдущая версия серверной части с обратной совместимостью по API и управляемая деградация витрины. Условия отката и ответственную роль согласовывают до распродажи, а сам откат проверяют под нагрузкой на этапе прогонов.
