ТестированиеНагрузочное тестированиеИнженер по нагрузочному тестированию

Во время нагрузочного теста генератор должен отправлять 100 новых операций в секунду, но при тайм ауте повт...

Во время нагрузочного теста генератор должен отправлять 100 новых операций в секунду, но при тайм-ауте повторяет запрос. Как это искажает оценку предельной пропускной способности? Рассмотрите фрагмент модели клиента:

for operation in workload:
    for attempt in range(3):
        response = send(operation, timeout=1.0)
        if response.ok:
            break
Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Повторы превращают 100 запланированных операций в потенциально большее число фактических запросов. Поэтому нагрузка на сервис и измеренный RPS могут превышать целевой уровень, а ошибки и задержки — частично быть следствием самого механизма повторов. Предельную пропускную способность нужно оценивать по первичным операциям отдельно от попыток и явно фиксировать политику повторов.

Исторический контекст

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

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

Постановка проблемы

В коде одна операция может породить до трёх запросов. Если первые две попытки завершились тайм-аутом, сервис уже получил дополнительную нагрузку, хотя с точки зрения бизнеса это всё ещё одна операция.

Если отчёт считает каждый HTTP-запрос как успешную единицу пропускной способности, он смешивает первичные операции, повторные попытки, ошибки и окончательные результаты. Это может привести к ложному выводу, что система выдерживает целевой поток, хотя фактическая нагрузка на неё значительно выше или большая часть ресурсов уходит на бесполезные повторы.

Подробное решение

Нужно разделять как минимум три величины: число начатых бизнес-операций, число отправленных попыток и число успешно завершённых операций. Целевой RPS обычно задаётся для первичных операций, а нагрузка на сервис определяется количеством всех реально отправленных запросов.

При тайм-ауте клиент не знает, обработал ли сервер запрос. Повтор может быть отправлен одновременно с продолжающейся обработкой первой попытки, поэтому один бизнес-запрос способен занять ресурсы сервиса несколько раз. Для неидемпотентных операций повтор дополнительно создаёт риск дублирования действий, если сервер выполнил первый запрос, но ответ потерялся.

В минимальной модели метрики следует собирать раздельно:

  • операции — число первичных запросов от сценария;
  • попытки — все отправленные запросы, включая повторы;
  • успешные операции — операции с приемлемым итоговым результатом;
  • доля повторов — отношение повторных попыток к первичным операциям;
  • конечные ошибки и тайм-ауты — результат операции после исчерпания политики повторов.

Для теста производительности возможны разные варианты. Полностью отключить повторы проще для поиска предела самого сервиса, но такой тест не отражает реальное поведение клиентов. Воспроизвести рабочую политику повторов реалистичнее, однако тогда нужно ограничивать их число, задавать задержку или экспоненциальную паузу и анализировать их вклад отдельно.

for operation in workload: attempts = 0 while attempts < 3: attempts += 1 result = send(operation, timeout=1.0) metrics.attempts += 1 if result.ok: metrics.completed_operations += 1 break metrics.primary_operations += 1

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

Ситуация из практики

Сервис проверяли при целевых 100 первичных операциях в секунду. При тайм-ауте клиент делал две повторные попытки без паузы, а отчёт показывал только общий RPS HTTP-запросов. На высокой нагрузке доля тайм-аутов росла, поэтому число фактических запросов увеличивалось именно в момент приближения к пределу системы.

Рассматривались три варианта. Отключить повторы — хороший способ измерить базовую пропускную способность сервиса, но он не показывает пользовательский результат. Оставить повторы без дополнительных метрик — наиболее реалистично внешне, но результаты становятся неверно интерпретируемыми. Разделить первичные операции и попытки, ограничить число повторов и добавить задержку — требует более сложной модели, зато позволяет видеть и предел сервиса, и влияние клиентской политики.

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

Что кандидаты часто упускают

  1. Всегда ли повтор увеличивает пропускную способность?

Нет. Повтор увеличивает число попыток и иногда число успешно завершённых бизнес-операций, но не обязательно повышает способность сервиса обрабатывать новую работу. При перегрузке он может усилить конкуренцию за CPU, соединения и очередь, вызвав ещё больше тайм-аутов. Поэтому повтор — механизм повышения вероятности результата, а не источник производительности.

  1. Чем опасны повторы без задержки между попытками?

Они создают синхронный всплеск нагрузки: после тайм-аута большое число клиентов почти одновременно отправляет новые запросы. Это может поддерживать перегрузку даже после кратковременного сбоя и вызывать каскад тайм-аутов. Пауза с ограничением числа попыток снижает такую амплификацию, но увеличивает время завершения операции.

  1. Можно ли безопасно повторять любой запрос?

Нет. Повтор относительно безопасен только при гарантированной идемпотентности операции либо при наличии механизма дедупликации, например идентификатора идемпотентности. Для операции, которая изменяет состояние, потеря ответа не означает, что сервер не выполнил действие, поэтому автоматический повтор может создать дубликат. В нагрузочном тесте это нужно учитывать как часть корректности сценария, а не только как настройку производительности.