В распределённой трассировке один запрос содержит параллельные вызовы: как определить, какой из них ограничивает сквозную задержку?
Нужно искать не самый долгий отдельный вызов, а участок критического пути — последовательность ожиданий, определяющую момент формирования итогового ответа. Для параллельных вызовов это обычно самый поздно завершившийся обязательный путь с учётом собственного времени обработки, ожидания очереди и вложенных зависимостей.
Водопад трассировки помогает увидеть перекрытие интервалов. Медленный вызов, который выполняется параллельно и завершается раньше критического пути, может почти не влиять на сквозную задержку.
В распределённых системах один пользовательский запрос проходит через несколько процессов и сетевых взаимодействий. Отдельные журналы и метрики хорошо показывают состояние компонентов, но без общей связи между операциями трудно установить, какие именно ожидания вошли в задержку конкретного запроса.
Распределённая трассировка появилась как способ связать операции единым идентификатором и представить их временную структуру: входящий запрос, вложенные вызовы, ожидание очередей и обращения к зависимостям. Это позволяет анализировать задержку по причинной цепочке, а не только по средним показателям отдельных сервисов.
Пусть сервис одновременно обращается к нескольким обязательным зависимостям. Итоговый ответ нельзя отправить, пока не завершится необходимый набор вызовов, поэтому задержка определяется их зависимостями и перекрытием, а не простым сложением всех длительностей.
Если выбрать узким местом просто самый длинный span, можно оптимизировать операцию, которая не влияет на момент ответа. Обратная ошибка — смотреть только на серверное время зависимости и пропустить ожидание соединения, очереди, сетевой передачи или блокировки на стороне вызывающего сервиса.
Сначала нужно восстановить дерево или граф трассировки и определить обязательные операции, от которых зависит ответ. Затем анализируют временные интервалы: для параллельных ветвей критичной обычно становится ветвь, которая завершает последнюю обязательную работу; для последовательных ветвей учитывается вся цепочка ожиданий.
Важно разделять несколько составляющих span: время постановки в очередь, ожидание свободного соединения, сетевую задержку, обработку на сервере и ожидание вложенных вызовов. Если родительский span намного длиннее дочерних, оставшаяся часть может указывать на локальную сериализацию, блокировку, преобразование данных или неинструментированное ожидание.
Практический анализ обычно выглядит так:
Есть несколько ограничений. При неполной инструментированности часть ожиданий будет ошибочно выглядеть как время родительской операции. В распределённой системе возможны погрешности синхронизации часов, а выборочное семплирование может не сохранить редкие медленные трассы. Поэтому трассировку следует дополнять метриками задержки и насыщения, а для расследования хвоста — сохранять или выбирать медленные трассы по понятным правилам.
Оптимизация критического пути имеет компромиссы. Уменьшение задержки некритичной параллельной ветви улучшит ресурсные показатели, но может не изменить пользовательское время ответа. Иногда выгоднее сделать зависимость необязательной, кэшировать результат или ограничить её влияние тайм-аутом, однако это уже меняет корректность и полноту ответа.
У API вырос p99 сквозной задержки. Трасса показывала три параллельных обращения: к профилю, рекомендациям и проверке прав. Рекомендации иногда занимали больше всех, но завершались до проверки прав, поэтому их ускорение почти не меняло время ответа.
Рассматривались три варианта. Масштабировать сервис рекомендаций было полезно для его собственной нагрузки, но дорого и слабо влияло на p99 API. Увеличить тайм-аут API было проще, однако это лишь дольше удерживало ресурсы и не устраняло задержку. Третий вариант — исследовать критический путь проверки прав, включая ожидание соединения с хранилищем.
Выбрали третий вариант: отдельно измерили получение соединения, ожидание внутри хранилища и обработку запроса. Выяснилось, что серверная логика была быстрой, а основная задержка возникала в очереди пула соединений. Устранение этого ожидания и настройка ёмкости пула уменьшили p99 API; масштабирование рекомендаций не потребовалось.
Да. Если он выполняется параллельно и заканчивается раньше другой обязательной ветви, его длительность не определяет момент отправки ответа. Критичным является не абсолютное время span, а его вклад в критический путь конкретного запроса.
Нужно искать большие интервалы у родительских span без соответствующих дочерних операций и проверять, какие этапы не инструментированы. Затем эти интервалы сопоставляют с метриками очередей, пулов соединений, сетевых тайм-аутов и загрузки ресурсов. Без такой проверки нельзя уверенно считать неизвестное время локальной обработкой.
Хвост распределения формируется редкими медленными запросами: ожиданием ресурса, повторными попытками, блокировками или перегруженными экземплярами. Среднее значение сглаживает такие случаи, поэтому для поиска узкого места сравнивают p95 или p99 трасс и их критические пути. При этом важно не смешивать трассы разных сценариев и учитывать, что редкие трассы могут быть потеряны из-за семплирования.