{ "index": 221, "slug": "editorial-2021-11-mechanism-load-testing", "title": "Почему «много запросов» не является нагрузочной моделью", "excerpt": "Разбор механизма workload: arrival intent, профиль сегментов, evidence packet и synthetic bottleneck. Почему aggregate без среды и stop criterion не объясняет ни причину, ни следующее действие.", "contentHtml": "
Проблема фразы «мы дали много запросов» в том, что она описывает объём шума, но не модель нагрузки. В ней не видно, когда возникал input, какой endpoint он представлял, какой набор данных использовался, что считалось завершением и кто наблюдал последствия. Цена — ложная причинность: aggregate меняется, а команда приписывает его очереди, базе или коду, хотя могла измениться сама среда или сценарий.
\nДля механизма достаточно одной строгой границы. В этой статье arrival означает намерение поставить planned request slot в сегмент профиля. Это не отправка запроса и не скорость. Fixture материализует slots в accepted/rejected synthetic units внутри local objects. Она не создаёт clock, HTTP, сеть, процесс, нагрузочный инструмент или telemetry. Поэтому слова latency-signal и error-signal ниже — диагностические ярлыки, а не измеренные метрики.
\nПолезная запись выглядит так: endpoint intent + data setup + environment constraint + ordered segments + stop criterion + evidence packet. Каждый элемент отвечает на отдельный вопрос. Endpoint ограничивает предмет. Data setup не даёт одному и тому же имени скрывать разные записи. Environment объясняет, при каком договоре существует наблюдение. Segments показывают переход. Criterion определяет, когда доказательство достаточно. Packet держит все поля рядом, чтобы вывод не зависел от памяти автора.
\nЕсли убрать любой элемент, получаем другой тип неопределённости. Без endpoint нельзя отличить чтение от изменения. Без данных нельзя повторить branch. Без среды невозможно увидеть drift. Без сегментов total не показывает порядок. Без criterion нельзя понять, почему модель закончилась именно здесь. Без packet остаётся пересказ, который невозможно проверить. Это не бюрократия вокруг теста: это минимальная структура причинной связи.
\n| Свойство | Профиль fixture | «Много запросов без сценария» | Инженерское последствие |
|---|---|---|---|
| Endpoint intent | один нейтральный путь и метод | не указан | нельзя проверить контракт входа |
| Arrival intent | slots принадлежат named segment | есть только total | нельзя увидеть переходы |
| Среда | identity и synthetic constraint записаны | не указана | любой drift маскируется под результат |
| Accepted / rejected | разложены по segment | не определены | неясно, что именно не прошло |
| Stop criterion | evidence после recovery | нет | конец наблюдения произволен |
| Interpretation | только training model | обычно звучит как verdict | риск выдать счётчик за throughput |
В реальном инструменте способ моделировать arrival зависит от executor, версии и конфигурации. Нельзя переносить значение одного параметра между tool без чтения его документации. В k6 v0.35.0 release notes отдельно связывают stage tags с конкретными executors; это исторический факт о версии, а не лицензия назвать любой массив slots её сценарием. Наша fixture специально не повторяет API инструмента. Она показывает только вопрос, который нужно сформулировать до выбора API: что означает появление следующего planned slot и как эта попытка будет отделена от результата обработки.
Разделение полезно и для закрытой модели пользователей, и для открытой модели arrival. В первом случае нужно назвать, кто ждёт завершения предыдущей итерации. Во втором — как tool ведёт себя при невозможности начать следующую работу. Но оба случая остаются неполными без data setup и environment. Число на оси не заменяет эту информацию. Поэтому article не предлагает универсальный executor и не выдаёт synthetic acceptance limit за реальную настройку генератора.
\nУ каждого segment fixture есть именованные slots, accepted units, rejected units, synthetic bottleneck flag и boundary text. Наличие flag не превращает rejection в ошибку приложения. Это самопроверка модели: step был объявлен как учебная граница до materialization, а не задним числом назван узким местом после просмотра результата. Такая разница особенно важна в текстах про производительность, где красивый график часто даёт больше уверенности, чем его исходные условия.
\nПроверка reconciliation предельно простая: длина plannedRequestSlots должна равняться acceptedUnits + rejectedUnits. Она не измеряет полезную работу и не заменяет server-side evidence. Зато она ловит редакционную ошибку: если часть plan исчезла между профилем и таблицей, автор больше не может честно сказать, что сравнил одно и то же. Именно такие мелкие несовпадения потом превращают нагрузочную заметку в набор несвязанных сигналов.
Наблюдаемость здесь не означает автоматически подключённый dashboard. Сначала нужны хорошо названные поля: endpoint intent, environment identity, data identity, segment name, planned slots, accepted/rejected units и stop state. Затем выбирается настоящий инструмент, который умеет сохранить требуемый raw signal и его контекст. OpenTelemetry Metrics API в historical tag v1.0.0 подчёркивает, что instrument задаёт смысл measurement, а не только форму числа. При этом документ помечен experimental; он не может быть основанием приписать старой системе готовую телеметрию.
Evidence packet полезен потому, что связывает две шкалы: plan и observation. План отвечает, что хотели проверить. Observation отвечает, какие учебные units были materialized. Если packet не содержит environment или criterion, визуализация всё равно может существовать, но reader уже не знает, какую именно гипотезу она проверяет. Поэтому packet не экспортируется как trace и не изображает работу настоящего мониторинга: это object для детерминированной проверки редакционной модели.
\nStep в fixture получил пять slots при явном limit в три accepted synthetic units. Модель оставляет два rejected units и поднимает единственный syntheticBottleneckFlag. Это намеренно скучный результат. Его задача — проверить четыре свойства: rejected units видны, flag находится в правильном segment, recovery идёт после step, stop criterion не срабатывает раньше. Если бы модель всегда принимала всё, она не проверяла бы путь, в котором author обязан объяснить границу.
Неправильный вывод звучал бы так: «мы нашли bottleneck и latency выросла». У fixture нет времени, сервера или наблюдаемой очереди, поэтому такой вывод нельзя получить. Правильный вывод уже: «план содержит заранее отмеченную synthetic boundary; для реального расследования нужны environment manifest, raw output выбранного tool и отдельный источник latency-signal». Скромная формулировка оставляет место для следующего эксперимента, а не подменяет его.
\nЭтот пример читает mechanism, а не запускает testing software. Он выводит путь как intent, явное ограничение среды и stop object. Последняя assertion возвращает false для aggregate, где есть только synthetic total. Так мы проверяем не способность «создать много», а способность не называть нагрузочной моделью то, что не содержит сценария.
\nconst fixture = runLoadTestingFixture();\nconst packet = fixture.evidencePacket;\n\nconsole.log(packet.endpoint.path);\n// /fixture/neutral-resource — intent only; no HTTP call exists in the fixture\nconsole.log(packet.environment.explicitConstraint);\n// at most three accepted synthetic units in one planned segment\nconsole.log(packet.stop);\n// stopped-by-criterion after recovery evidence is present\n\nif (!fixture.assertions.badAggregateRejected) {\n throw new Error("a count without scenario was accepted as a model");\n}\nВ коде нет fetch, http, setTimeout, файла или child process. Это не ограничение языка и не рекомендация для production. Это защита смысла fixture: добавление реального вызова сделало бы её зависимой от внешнего состояния и позволило бы принять случайный ответ за доказательство. Реальный инструмент нужно запускать отдельной операцией с отдельным пакетом условий, а не прятать в редакционный self-check.
Fixture не сообщает, как ведут себя executors k6, как рассчитываются thresholds, как именно агрегирует метрики OpenTelemetry или какие свойства имеет конкретный server. Ссылки на k6 v0.35.0 и его samples/thresholds.js зафиксированы, чтобы не ссылаться на mutable current documentation. Они нужны только для historical boundary и для требования называть metric вместе с порогом. Пакет не исполняет k6, не создаёт VU и не использует его API.
Не моделируются HTTP, сеть, TLS, DNS, scheduler, queue, CPU, память процесса, база, cache, browser, пользователь, latency, throughput, error rate, capacity, production incident или результат benchmark. RFC 2330 добавлен как внешняя рамка аккуратного определения метрик, но он относится к IP performance metrics и не определяет готовность application endpoint. Следующий шаг — описать один реальный tool-run отдельно, не смешивая его raw output с объектами этой учебной модели.
\n