Программа курса

Модуль 06 / Урок 24 из 27

Span references и асинхронные процессы

Теория и практика

Дерево «родитель-ребёнок» — не единственная связь спанов. Для fan-out, батчей и фоновых работ существуют ссылки child-of и follows-from — и умение их выбирать отличает зрелую трассировку.

Два типа ссылок

СсылкаСмыслПример
ChildOfродитель логически «ждёт» результата ребёнка (даже если параллельно): время ребёнка входит во время родителяfan-out HTTP-вызовы внутри запроса; подзапросы батча, чей результат нужен ответу
FollowsFromродитель только «причинно предшествует»: его результат уже не влияет на исполнение; ребёнок живёт своей жизньюотправка аудита в шину; кеширующий префетч после ответа; async-обработка события

В OTel API различие почти стерлось (все ссылки задаются как SpanLink/parent), но семантически выбор важен: он диктует, как читать трейс и как считать критический путь.

Fan-out: несколько родителей

Классика: агрегатор запускает 3 параллельных запроса. В визуализации это выглядит как несколько спанов-детей на одном уровне — но каждый ребёнок имеет одного parent. Для «спан с несколькими родителями» используются span links:

# Python: спан с дополнительной ссылкой на другой спан
from opentelemetry.trace import Link, SpanContext, TraceFlags

link = Link(other_span.get_span_context())
with tracer.start_as_current_span("merge_results", links=[link]):

Links полезны: «батч собран из N исходных запросов», «повтор после таймаута связан с исходным трейсом», «крон-задача обработала 100 трейсов прошлого дня». Ссылки видны в UI в панели спана и переходят по ним кликом.

Асинхронные задачи: главный кейс

Паттерн «запрос породил фоновую задачу» — самая частая точка потери связности:

  • Один контекст на задачу: при постановке в очередь снабдите сообщение контекстом (traceparent в свойствах) — консюмер продолжит тот же трейс (урок 6.2);
  • Задача живёт дольше трейса? Нормально: трейс завершён, «продолжение» — новый спан с FollowsFrom-ссылкой. Итог в UI: два трейса, связанные ссылкой;
  • Планировщик: cron-джоба — корень собственного трейса (родитель — сам планировщик); для читабельности добавляйте атрибут schedule.name.
⚠️ Не «продлевайте» трейс бесконечно: трейс — история одного запроса. Многочасовая батч-джоба с 10 000 спанов в одном трейсе нечитаема — режьте на этапы-трейсы со ссылками между ними.

Практика

  1. Реализуйте fan-out: два параллельных вызова из одного endpoint'а; убедитесь, что в UI спаны на одном уровне и критический путь = max из них.
  2. Добавьте спан-линк: фоновая задача, обработавшая заказ, ссылается на исходный корневой спан; проверьте переход по ссылке в UI.
  3. Сделайте «батчовый» трейс: один спан-родитель + 100 детей-элементов — оцените читаемость, затем сравните с вариантом «агрегирующий спан + события» и сформулируйте правило выбора.
Мини-квиз

1. Аудит-сообщение ушло в шину после ответа пользователю: ChildOf или FollowsFrom? — FollowsFrom: родитель не ждёт, ответ уже отдан.

2. Что такое span link и когда нужен? — Дополнительная связь спана с чужим контекстом: батчи, повторы, слияние результатов.

3. Почему длинную джобу не трассируют одним трейсом? — Читаемость и инструменты: этапы — отдельные трейсы, связанные ссылками.