После теста часто появляется короткое сообщение: «ошибок стало больше» или «latency ухудшилась». Сам сигнал ещё не говорит, где причина. Цена поспешного диагноза — поменять лимит, базу или код на основании наблюдения, которое пришло из другой среды, другого сценария или вообще не было измерением. Сначала нужно сохранить то, что именно видел автор, а не выбирать виновника по знакомому слову.
Здесь error-signal и latency-signal — только диагностическая модель. Fixture показывает synthetic rejected units в step и прямо хранит latencySignal: unavailable-in-example. В ней нет clock, HTTP, server, load tool, сети, базы, telemetry или production incident. Поэтому она не может сказать, что latency росла, а rejection не равен реальной ошибке. Её смысл — разложить проверку причин до того, как появятся данные настоящего запуска.
Сначала сохраняем evidence, потом меняем систему
Минимальный evidence packet состоит из endpoint intent, environment identity, data setup, профиля, stop criterion, записи accepted/rejected units и diagnostic card. Это не бюллетень для руководителя и не dashboard. Это порядок чтения фактов. Если signal нельзя привязать к endpoint и environment, он ещё не готов для вывода о приложении. Если неизвестен data setup, нельзя отличить изменение входа от изменения реализации. Если отсутствует criterion, нельзя доказать, что наблюдение завершилось по тому же правилу, по которому началось.
Перед изменением полезно добавить два отрицательных вопроса. Что могло поменяться вне кода? И что в самом сценарии может создавать видимость проблемы? Первый вопрос ведёт к environment drift: иной build, настройка, зависимость или manifest. Второй — к scenario gap: total без сегментов, другой endpoint intent, данные без identity или пропущенный recovery. Только после этих проверок можно обсуждать локальную границу, и даже тогда фиксировать её как hypothesis, а не как уже найденный bottleneck.
| Кандидат | Что увидеть в evidence | Чего пока нет | Rollback-safe действие |
|---|---|---|---|
| Environment drift | identity среды не совпадает с declared envelope | доказательства проблемы приложения | вернуть сравнение к зафиксированному manifest; не менять endpoint |
| Scenario gap | нет сегмента, data identity, criterion или endpoint intent | сопоставимого workload | восстановить последнюю полную карточку и повторить чтение packet |
| Synthetic bottleneck | flag в step и reconciliation slots с units | доказательства очереди или latency реального сервиса | менять только один model constraint или segment, затем собрать packet заново |
| Неизвестная реальная причина | есть raw signal, но не хватает context | достаточной связи между source и effect | остановить широкий change и назначить один измеримый следующий шаг |
Environment drift: сравниваем договор, а не название стенда
Environment drift не надо сводить к «стенд сломан». Сначала сравнивают identity, configuration boundary и присутствие внешних систем с тем, что записано в запуске. В fixture среда называется isolated-training-envelope-v1, а external system помечена not-present. Если кто-то позже читает packet как результат серверного запроса, уже есть явное противоречие: такая интерпретация нарушает договор модели.
Безопасное действие здесь обычно скучное: остановить изменение приложения и вернуть исследование к declared manifest. Это rollback-safe, потому что меняется не рабочая система, а вывод о ней. Нельзя чинить connection pool или добавлять cache, пока не доказано, что наблюдение пришло из той среды, для которой эти действия вообще имеют смысл. Такой шаг не решает настоящую проблему, но он убирает ложную ветку расследования.
Scenario gap: total не заменяет путь пользователя
Если packet содержит только total, ситуация ещё хуже: нельзя выяснить, был ли warm-up, как выглядел steady, что именно менялось на step и был ли recovery. Агрегат может совпасть с другим запуском случайно, но это не делает их сравнимыми. Правильная реакция — не усреднять ещё сильнее, а восстановить пропавшую структуру: endpoint intent, data setup, segment order, planned slots, правило accepted/rejected и criterion остановки.
В fixture bad aggregate называется many-requests-without-scenario и сразу получает verdict acceptedAsLoadModel: false. Это не осуждение коротких тестов. Коротким может быть и корректный сценарий, если у него ясная граница. Отклоняется другое: попытка назвать диагностикой число, для которого невозможно рассказать, откуда оно взялось и какое действие оно вправе изменить.
Synthetic bottleneck: видим границу, не придумываем причину
Step fixture содержит больше planned slots, чем разрешает declared synthetic constraint. Поэтому два units отмечены rejected, а flag включён только у этого segment. Это достаточное evidence, чтобы проверить согласованность модели: slots не исчезли, rejection не попал в warm-up, recovery записан после step. Но этого недостаточно, чтобы говорить о thread pool, блокировке, network saturation или ответе базы. Все эти слова относятся к системам, которых здесь нет.
Полезный вопрос после flag звучит так: «какая реальная граница могла бы соответствовать этой гипотезе и каким независимым сигналом её проверить?» Ответ должен быть один. Например, сначала зафиксировать environment и вход, затем выбрать инструмент и raw signal, затем проверить конкретную зависимость. Не надо одновременно менять retry, таймаут, SQL и число генераторов. Широкое изменение уничтожит связь между гипотезой и результатом, даже если итоговое число станет приятнее.
Минимальный JS: получить кандидатов, не выполнить изменение
Пример ниже выводит кандидаты из local diagnostic card. Он не собирает новую телеметрию и не запускает correction. Это удобно для review: читатель видит, какое evidence потребуется для каждой ветки и какое действие можно откатить без вмешательства в неизвестную инфраструктуру. Проверка latency-signal в конце намеренно требует unavailable-in-example.
const fixture = runLoadTestingFixture();
const diagnosis = fixture.evidencePacket.diagnosticModel;
console.table(diagnosis.candidates.map((candidate) => ({
candidate: candidate.name,
evidenceNeeded: candidate.evidenceNeeded,
rollbackSafeAction: candidate.rollbackSafeAction,
})));
if (diagnosis.observedSignals.latencySignal !== "unavailable-in-example") {
throw new Error("the training model was accidentally presented as latency");
}
Rollback-safe здесь означает обратимость расследования. Для drift мы возвращаемся к declared manifest. Для scenario gap — к последней полной карточке. Для synthetic boundary — к одному изменённому model constraint или segment. Все три действия сохраняют original packet и не требуют менять runtime системы. Если настоящая среда уже изменилась, её откат — отдельная операция с owner, правами и планом; из учебного object такой команды не получить.
Как выбрать одно действие после классификации
Если environment identity различается, следующее действие — зафиксировать разницу и не сравнивать numbers между envelope. Если отсутствует сценарий, действие — восстановить route и проверить assertions, не запускать ещё один «большой» total. Если synthetic flag единственный, порядок segments корректен, а slots reconcile, можно изменить одну переменную модели и посмотреть, остаётся ли packet читаемым. Если ни одна ветка не подтверждена, verdict должен остаться «недостаточно evidence». Это нормальный результат расследования, а не провал.
После появления настоящего raw output порядок не меняется. Нужно добавить точную версию инструмента, command/configuration, окружение, data setup, окно наблюдения и способ связать signal с endpoint. И только потом сопоставлять observed error или latency с конкретной гипотезой. Документация k6 v0.35.0 и historical OpenTelemetry tag помогают не спутать название инструмента со свойством модели, но ни один из этих источников не содержит результат вашего endpoint.
Маршрут: симптом → причина → проверка → действие
- Симптом. Появился error-signal или latency-signal, но его origin, среда или сценарий не приложены к сообщению.
- Причина-гипотеза. Сначала разделите environment drift, scenario gap и явную synthetic boundary. Не называйте реальный bottleneck до независимого evidence.
- Проверка среды. Сверьте environment identity и constraint с declared packet. При несовпадении остановите вывод о коде.
- Проверка сценария. Проверьте endpoint, data setup, порядок warm-up → steady → step → recovery, criterion и reconciliation slots с units.
- Проверка границы. Убедитесь, что flag находится только в объявленном step и diagnostic model не называет units latency или throughput.
- Действие. Выберите одно обратимое действие для подтверждённой ветки; сохраните исходный packet. Для реальной причины подготовьте отдельный измеримый запуск.
- Ожидаемый результат. Следующий change связан с проверяемой гипотезой, а не с самым тревожным словом на графике.
Историческая граница и ограничения
Использованные ссылки намеренно закреплены на версиях до конца ноября 2021 года. k6 v0.35.0 опубликован 17 ноября; его sample объясняет, что threshold связывается с выбранной метрикой внутри инструмента. OpenTelemetry v1.0.0 Metrics API в historical snapshot имеет experimental status, а Trace API задаёт терминологию trace/span. Эти документы помогают формулировать вопросы к настоящей телеметрии, но не делают packet реализацией k6 или OpenTelemetry.
Не было реального прогона, latency, RPS, error rate, пользователей, capacity, production incident или raw result. Fixture не выполняет HTTP, сеть, tool, server, database, browser, clock, filesystem или process. Synthetic rejected units не равны HTTP error; latency-signal зафиксирован как not collected. Следующий проверяемый шаг — подготовить отдельный real-test protocol с владельцем, разрешённой средой, rollback plan и способом хранить raw evidence. До него честнее оставить verdict узким: модель проверила собственный порядок, не систему.
Проверяемые источники
- Grafana k6 v0.35.0: versioned release record — релиз опубликован 17 ноября 2021 года; он задаёт историческую верхнюю границу для упоминания инструмента и не является результатом прогона этой fixture
- Grafana k6 v0.35.0: fixed source snapshot samples/thresholds.js — точный commit, на который указывает тег v0.35.0; пример связывает порог с именованной метрикой, а пакет не импортирует k6 и не исполняет его
- OpenTelemetry Specification v1.0.0: fixed Metrics API snapshot — снимок от февраля 2021 года помечает Metrics API как experimental и отдельно описывает смысл измерения, instrument и aggregation; fixture не реализует OpenTelemetry
- OpenTelemetry Specification v1.0.0: fixed Trace API snapshot — исторический документ для терминов trace и span; evidence packet в этой статье является локальным object, а не экспортированной трассой
- RFC 2330: Framework for IP Performance Metrics — нормативная рамка для аккуратного определения измеряемого свойства; RFC относится к IP-метрикам и не превращает учебный endpoint в capacity benchmark