Закон Литтла: почему число виртуальных пользователей не задаёт нагрузку
Если зафиксировать количество виртуальных пользователей, вы не управляете нагрузкой. Это следует из формулы 1961 года, и это арифметика, а не мнение.
«Поставим двести виртуальных пользователей» — самая частая фраза при планировании нагрузочного теста. И самая бессодержательная.
Двести виртуальных пользователей — это не нагрузка, а ограничение сверху на число одновременно висящих запросов. Сколько запросов в секунду из этого выйдет, решаете уже не вы.
Формула
Джон Литтл доказал её в 1961 году в статье «A Proof for the Queuing Formula: L = λW» в журнале Operations Research.
L = λW
Здесь L — среднее число заявок в системе, λ — средняя интенсивность их поступления, а W — среднее время, которое заявка в системе проводит.
На язык нагрузочного тестирования переводится так:
виртуальные пользователи = запросы в секунду × время ответа
Красота этой формулы — в том, чего в ней нет: она не зависит ни от распределения интервалов между заявками, ни от распределения времени обслуживания, ни от порядка обработки очереди. Требуется одно — стационарность системы, и это делает формулу применимой почти везде, где есть очередь.
Что из неё следует для теста
Возьмём цифры. Вы хотите проверить сервис на 500 запросах в секунду. Время ответа в норме 200 мс.
L = 500 × 0.2 = 100 виртуальных пользователей.
Ставите сотню, запускаете, получаете свои 500 RPS. Пока всё сходится.
Теперь система начинает деградировать, и время ответа растёт до 400 мс. Число виртуальных пользователей вы не меняли, оно так и сто. Подставляем:
λ = 100 / 0.4 = 250 запросов в секунду.
Нагрузка упала вдвое. Сама. Ровно в тот момент, когда система начала испытывать трудности, генератор перестал её нагружать.
Почему это ломает именно то, что вы проверяете
Смысл нагрузочного теста в том, чтобы узнать, как система ведёт себя под заданной нагрузкой. Закрытая модель этот замысел выворачивает наизнанку: нагрузка становится не заданной, а производной от поведения системы.
Получается тест, который жалеет подопытного: чем хуже отвечает сервис, тем меньше на него давят. Найти точку, где он ломается, таким тестом нельзя в принципе — генератор тормозит вместе с ним.
Это тот же механизм, что и в coordinated omission, только с другой стороны: там генератор переставал измерять, здесь он перестаёт нагружать. Причина одна — темп привязан к скорости ответа.
Как задать нагрузку по-настоящему
Фиксировать надо не L, а λ. То есть не число пользователей, а темп поступления запросов.
В k6 за это отвечают исполнители constant-arrival-rate и ramping-arrival-rate. Документация описывает их как фиксированное число итераций за заданное время. Противоположность им — constant-vus, где виртуальные пользователи выполняют столько итераций, сколько успеют.
Разница в формулировке и есть разница между «я задаю нагрузку» и «я задаю ограничение, а нагрузку посчитает система».
Где формула не работает
Стационарность — не формальность в мелком шрифте.
Во время разгона система не стационарна, и подставлять в формулу цифры с участка нарастания бессмысленно. Считать надо по установившемуся режиму, а не по первым тридцати секундам.
Второе ограничение практическое. Формула даёт среднее, а среднее по времени ответа скрывает хвост распределения. Сотня виртуальных пользователей при среднем в 200 мс это правда, но если у вас бимодальное распределение с половиной запросов по 10 мс и половиной по 390 мс, поведение системы будет совсем не тем, что вы планировали.
Что с этим делать завтра
Откройте свой последний отчёт о нагрузочном тесте и найдите там три числа: заданное число виртуальных пользователей, полученный RPS и среднее время ответа.
Перемножьте второе на третье.
Если результат заметно меньше первого, значит виртуальные пользователи простаивали и вы недодали нагрузки. Если сходится, но RPS вышел ниже целевого, значит вы упёрлись не в систему, а в собственную закрытую модель.
Источники
Доклады по теме
- Нагрузочное тестирование для котиков. Scenario- vs Hit-Based-подходы — Иван Приходько
- Подводные камни в нагрузке — Владимир Ситников
- Нагрузочное тестирование — Григорий Липин