{ "index": 59, "slug": "editorial-2026-05-mechanism-systems-performance", "title": "Почему короткий trace не доказывает ускорение системы", "excerpt": "Как отделить ожидание от работы, проверить сопоставимость нагрузки и остановить разбор, когда trace не подтверждает причинный вывод.", "contentHtml": "
Фраза «БД медленная» часто появляется после одного взгляда на trace. На ней виден длинный промежуток, но не видно, ждёт ли запрос очередь, выполняет ли БД работу или задерживается внешний вызов. Цена ошибки — недели оптимизации не того участка. Команда меняет SQL, а пользовательский путь не становится короче.
\nНадёжный разбор начинается с более узкого тезиса: длительность показывает интервал, но не его причину. Чтобы сравнить два результата, нужно одновременно сохранить связную trace, одинаковую границу нагрузки и ограниченный вывод. Если одно условие нарушено, правильное действие — остановиться и назвать недостающий факт.
\nРассмотрим учебный пример. Root span описывает путь запроса от входа до ответа. Внутри него идут три последовательных интервала: admission queue ждёт 520 условных единиц, вызов БД занимает 150, внешний каталог — 200. Эти единицы придуманы для примера. Они не являются миллисекундами, метрикой сервиса или результатом production-измерения.
\n| Сегмент | Роль | Интервал | Допустимый вывод |
|---|---|---|---|
| fixed-admission-queue | queue-wait | 40–560, 520 units | Самый длинный названный сегмент этого input |
| fixed-db-call | database-execution | 570–720, 150 units | Интервал вызова БД в этой trace |
| fixed-catalog-call | external-dependency | 730–930, 200 units | Интервал внешнего вызова в этой trace |
| fixed-gateway | end-to-end | 0–1000, 1000 units | Граница пути, но не объяснение причины |
Таблица не говорит, что очередь замедляет production. Она не говорит, что SQL нужно переписать. Она фиксирует структуру одного учебного input. Это важное различие. Длинный span можно ранжировать. Причину нужно проверять отдельным экспериментом с той же границей.
\nДопустим, второй root равен 800 вместо 1000. На графике он выглядит лучше. Но во втором input одновременно изменились cohort, число логических запросов с 12 до 24, concurrency с 3 до 6 и форма чтения. Такой результат нельзя приписать предполагаемой оптимизации. Он описывает другой сценарий.
\nКонтрольная граница должна быть записана до сравнения длительностей. Для этого учебного примера она состоит из четырёх полей: cohort=fixed-load-a, logicalRequests=12, concurrency=3 и inputShape=fixed-read-shape-a. Это не универсальный стандарт нагрузки. Это минимальный контракт конкретной проверки. В другом сценарии набор полей будет иным, но правило останется тем же: заранее назвать условия, которые должны совпасть.
const boundary = {\\n cohort: 'fixed-load-a',\\n logicalRequests: 12,\\n concurrency: 3,\\n inputShape: 'fixed-read-shape-a',\\n};\\n\\n// Учебный пример: отсутствие совпадающей границы\\n// запрещает называть разницу эффектом изменения.\\nconst comparable = sameBoundary(baseline, candidate, boundary);\nКод выше иллюстрирует только порядок проверки. Функция sameBoundary не измеряет latency и не находит bottleneck. Она отвечает на более простой вопрос: можно ли поставить два учебных результата рядом. В реальном проекте нужно явно определить сравниваемые поля, единицы измерения и правила обработки пропусков.
| Симптом | Возможная причина | Проверка | Действие |
|---|---|---|---|
| Длинный интервал перед работой | Ожидание в admission queue | Есть отдельный span с ролью queue-wait и parent внутри root | Оставить наблюдение; не называть очередь причиной production-задержки |
| Длинный span помечен только internal | Класс задержки неизвестен | Проверить роль и границы интервала | Вернуть stop-hidden-queue и назвать ожидание unknown |
| Root стал короче | Изменилась нагрузка или форма входа | Сверить cohort, requests, concurrency и input shape | При расхождении вернуть stop-incomparable-load |
| Дочерний span ссылается на отсутствующий parent | Trace неполна | Проверить связность дерева и интервалы | Вернуть stop-incomplete-trace; не восстанавливать связь догадкой |
| В записи есть «стало быстрее» | Заявлен эффект без контроля | Найти baseline с той же границей и явный критерий | Снять effect claim и оставить только наблюдение |
Неполный материал должен завершать разбор отказом. У учебного input incomplete-trace-v1 дочерний span указывает на отсутствующий parent. Нельзя определить, относится ли интервал к этому пути. Статус stop-incomplete-trace точнее, чем попытка соединить span по времени или имени.
У hidden-queue-v1 длинный интервал называется fixed-unclassified-delay. Он похож на ожидание, но такого сходства недостаточно. Статус stop-hidden-queue означает: сначала назовите границу ожидания, затем продолжайте. Иначе команда переложит время на очередь только потому, что она удобна как объяснение.
У incomparable-load-v1 root равен 800, но нагрузка отличается от baseline. Статус stop-incomparable-load не утверждает, что число 800 неверно. Он запрещает делать из него вывод об ускорении. Наконец, unsupported-effect-v1 содержит фразу faster-after-change без допустимого контрольного сравнения. Для него нужен stop-unsupported-effect.
for (const id of [\\n 'hidden-queue-v1',\\n 'incomparable-load-v1',\\n 'unsupported-effect-v1',\\n]) {\\n const result = reviewFixedPerformanceInput(createInput(id));\\n console.log(id, result.status, result.nextAction);\\n}\\n\\n// Учебный результат: stop — нормальный исход проверки.\\n// Он не доказывает, что система медленная.\nОтказ должен быть машинно и текстово видимым. Он сохраняет конкретную причину остановки и следующее действие. Общая фраза «нужно больше данных» хуже: она не показывает, какого именно условия не хватает.
\nДаже связная trace не сообщает автоматически, почему очередь заняла 520 units. Причиной может быть лимит ресурса, политика admission, форма учебного сценария или другая часть системы. Роль span CLIENT, SERVER или INTERNAL помогает описать операцию. Она не превращает интервал в диагноз и не назначает оптимизацию.
Правильная цепочка выглядит так: наблюдение — queue-wait=520; гипотеза — правило admission создаёт ожидание; новая проверка — заранее определённый input с той же границей и одним изменённым условием; результат — сравнимое наблюдение или новый stop. Перескок от первого пункта к утверждению «очередь является корнем проблемы» нарушает границу доказательства.
То же относится к БД. database-execution=150 не является SQL-профилем, индексом, бюджетом или обещанием. Это интервал вызова в учебной trace. Если нужен разбор SQL, он требует отдельного measurement contract, собственных входов и критерия сравнения.
Учебная trace не даёт распределения latency, хвостов, throughput, variance, queue discipline или стоимости ресурсов. Условные units нельзя переводить в миллисекунды. Один root не заменяет серию измерений. Связь через trace-id не гарантирует полноту дерева. Пересекающиеся span нельзя бездумно складывать: они могут выполняться параллельно.
\nИсточники ниже описывают контекст trace, роли span и семантику HTTP. Они не доказывают bottleneck, не обещают latency и не подтверждают production-эффект. Поэтому материал ограничивает вывод учебным input и не предлагает rollout, изменение конфигурации или выбор индекса.
\nРазбор готов, если другой инженер получает тот же status на том же именованном input, видит связное дерево, понимает контрольную границу и может указать, какое условие приводит к stop. В принятом учебном случае вывод должен остаться наблюдением: queue wait — самый длинный названный сегмент в данной trace; причинность и эффект изменения не заявлены. Если для чтения вывода нужны устные пояснения, внешний дашборд или догадка автора, материал не готов.
\n