DarkRiDDeR19 мин

Время ожидания, время работы и ложное сравнение нагрузки

ПроизводительностьНадёжность

Проблема начинается с фразы «БД медленная», когда trace показывает только длинный промежуток. Цена этой подмены — оптимизация исполнения при реальном ожидании в очереди либо при внешнем вызове. Механика performance review начинается не с percentiles и не с profiler: она требует разделить ожидание, работу и зависимость в одной сопоставимой synthetic trace.

Здесь три независимые оси: роль сегмента, нагрузочная граница и вывод. Роль отвечает, что именно названо в литерале; нагрузка — можно ли поставить рядом baseline; вывод — какое утверждение допускает доказательство. Если оси смешать, короткий root span при иной concurrency легко выдают за ускорение.

Три вида времени, которые нельзя складывать в одну причину

Queue wait — время до начала работы в названной admission queue. Database execution — интервал вызова БД в synthetic trace. External dependency — интервал исходящего вызова каталога. Их длительности можно расположить рядом; причинный ярлык переносить нельзя. CLIENT и SERVER из OpenTelemetry — описательные kinds, а не автоматическая классификация bottleneck.

От наблюдения к допустимому высказыванию
Наблюдение в fixed literalЧего этого недостаточно доказатьРазрешённая формулировкаСтоп-причина
queue-wait 520 unitsчто очередь замедляет productionожидание — длиннейший названный сегмент данного inputнет иной нагрузки
db-call 150 unitsчто SQL надо оптимизироватьисполнение БД занимает 150 units в этой записинет причины
root 800 вместо 1 000что изменение ускорило путьдругой input имеет другой интервалdifferent cohort/load
unclassified-delay 520что это очередьзадержка не классифицированаhidden queue
Схема двух synthetic нагрузочных точек и latency: несопоставимое сравнение перечёркнуто
Рисунок 2. Разная нагрузка и форма входа не образуют контрольную границу, даже если второй интервал короче.

Проверка сопоставимости

Для допуска к сравнению литерал хранит cohort fixed-load-a, 12 logical requests, concurrency 3 и fixed-read-shape-a. Это не универсальный набор метрик. Это минимальный контракт конкретного exercise. Смена хотя бы одного поля переводит результат в stop-incomparable-load, потому что разница может принадлежать нагрузке, а не предполагаемому изменению.

Fail-closed вместо правдоподобной догадки

У hidden-queue-v1 интервал 40–560 специально помечен unknown-delay. Он выглядит как очередь, но функция останавливается: stop-hidden-queue. У incomplete-trace-v1 дочерний span ссылается на отсутствующего parent — функция возвращает stop-incomplete-trace. Так review фиксирует недостающий факт, а не превращает форму диаграммы в диагноз.

import { createFixedPerformanceReviewInput, reviewFixedPerformanceInput } from './upgrade-2026-05.mjs';

for (const id of ['hidden-queue-v1', 'incomparable-load-v1']) {
  const result = reviewFixedPerformanceInput(createFixedPerformanceReviewInput(id));
  console.log({ id, accepted: result.accepted, reason: result.reasons[0] });
}

Длительность — не причинность

Длинный span говорит, что интервал записан длинным в конкретном литерале. Он не говорит, почему интервал получился длинным и какое изменение его сократит. Очередь может быть следствием выбранной дисциплины, ограниченного ресурса или искусственно заданного сценария; БД может ждать сеть; внешний вызов может включать локальную подготовку. Даже в одинаковой trace роль и причинный механизм — разные утверждения. Функция намеренно не принимает поле «cause», потому что fixture его не доказывает.

Полезный разрыв выглядит так: наблюдение — queue-wait = 520; гипотеза — «admission rule создаёт ожидание»; проверка гипотезы — другой заранее определённый synthetic literal с тем же контролем; эффект — только сравнение разрешённых результатов. Нельзя перескочить с первого пункта к четвёртому. Чем ярче диаграмма, тем важнее сделать этот разрыв видимым в записи.

Тот же принцип работает для БД. database-execution = 150 — не SQL diagnosis, не индекс и не бюджет. Это значение в условной шкале конкретного input. Оно может стать исходной точкой для отдельного вопроса, но в текущем пакете не получает рекомендаций. Такая неполнота является свойством качественного review: он не выдаёт отсутствующие причины за измеренные.

Связность, вложенность и параллельность

Complete tree проверяет минимальную структурную вещь: у дочернего span есть существующий parent, а время не идёт назад. Этого недостаточно, чтобы автоматически вычислить все возможные критические ветви. Мы не строим общий алгоритм распределённой трассировки и не интерпретируем overlap. В fixture интервалы заданы последовательно, потому что задача учебного review — показать разделение типов задержки, а не спрятать причинную ошибку за сложным графом.

Если два CHILD span пересеклись во времени, их нельзя бездумно сложить. Если один дочерний интервал выходит за root, это нарушение границы, а не «дополнительная производительность». Если producer и consumer разделены очередью, идентификаторы могут связывать события, но сам факт связи ещё не определяет время ожидания. W3C и OpenTelemetry помогают назвать связь и role; содержательный вывод всё равно требует явного measurement contract.

Почему короткий root не побеждает сам по себе

В incomparable-load-v1 root равен 800, то есть визуально лучше 1 000. Одновременно input меняет cohort, число logical requests, concurrency и input shape. Нельзя выбрать один из этих факторов как объяснение, потому что литерал не изолирует его. Возврат stop-incomparable-load не отрицает 800; он запрещает называть разницу эффектом предполагаемой оптимизации.

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

Механика намеренно предпочитает отказ частичному ответу. Удобный, но неполный trace создаёт больше риска, чем отсутствие цифры: его легко включить в убедительный narrative. Named stop reason сохраняет контекст недостающего условия и делает повторный review дешевле. В этой модели отказ не является ошибкой функции; он и есть корректный результат для входа, который нельзя безопасно интерпретировать.

Поэтому `stop` должен быть виден и в коде, и в тексте, и в таблице, а не прятаться в комментарии к графику.

Зачем нужна единая trace, а не три полезных фрагмента

Отдельный лог БД, внешний таймер и очередь из другой записи могут быть качественными артефактами, но не составляют критический путь. В W3C trace context trace-id предназначен для идентификации распределённой trace; parent-id связывает операцию с родителем. В нашем узком упражнении это переводится в проверяемое дерево литералов. Отсутствующий link — запрет на склейку, а не повод подобрать наиболее похожее число.

Нагрузка не является фоном

Logical requests, concurrency и input shape здесь не «подписи к графику». Это условия, при которых одно synthetic наблюдение разрешено поставить рядом с baseline. Рост logical requests с 12 до 24 и concurrency с 3 до 6 меняет сам вопрос: короткий root в incomparable-load-v1 не говорит, улучшился ли тот же путь. Он говорит лишь, что задан другой литерал.

Полезная привычка — писать контрольную границу раньше результата. Тогда фраза «root стал короче» сразу вызывает проверку: тот же cohort? те же logical requests? та же форма входа? Если хотя бы на один вопрос нет точного ответа, результат следует записать как отдельное наблюдение. Не нужно подбирать коэффициент, нормировку или убедительное объяснение, чтобы снять этот запрет.

  1. Сначала проверить связность одного synthetic дерева и интервалы каждого named span.
  2. Затем выписать load boundary до просмотра отличий root interval.
  3. Потом отделить queue-wait от database execution и external dependency.
  4. Только после этих проверок оформить наблюдение или вернуть точную stop reason.

Что именно проверяет функция

Функция не вычисляет throughput и не моделирует scheduler. Она проверяет структуру аргумента: complete tree, названную queue-wait, фиксированную нагрузочную границу и отсутствие effect claim. После этого она ранжирует уже имеющиеся условные интервалы. Такой порядок полезен для review: сначала валидность материала, затем интерпретация, но никогда не обратная последовательность.

Fail-closed ветви механики
УсловиеСтатусПочему не продолжаем
дочерний span без parentstop-incomplete-traceпуть нельзя собрать
cohort или concurrency иныеstop-incomparable-loadнет общей границы
длинный интервал unknown-delaystop-hidden-queueожидание не отделено от работы
заявлен faster-after-changestop-unsupported-effectнет доказанного эффекта

Граница утверждения

Даже complete и comparable input не утверждает, что найден bottleneck исправлен. Он разрешает один hand-off: «при заданных literals очередь — первая наблюдаемая кандидатная граница исследования». Рядом должны стоять условия сравнения и контрпример: при неизвестном ожидании или иной concurrency вывод отменяется. Это дешевле, чем спор о точности десятичного знака у несопоставимых наблюдений.

Проверяемые источники

  • W3C Trace Context — версия: Recommendation, 23.11.2021, immutable publication. Фиксирует формат traceparent и смысл trace-id/parent-id для связности распределённой trace. Граница: Опираемся только на идентификацию и связь; не выводим производительность из стандарта.
  • OpenTelemetry Specification Trace API — версия: v1.29.0, commit c6520a7, 11.01.2024. Задаёт различение CLIENT, SERVER, PRODUCER и CONSUMER span как описательных ролей. Граница: Роль span не доказывает причину задержки и не является метрикой.
  • RFC 9110: HTTP Semantics — версия: RFC 9110, June 2022. Определяет HTTP как stateless application-level protocol и отделяет протокольную семантику от измерения времени. Граница: Не используем RFC для обещаний latency, capacity или реального поведения системы.