Суть Читай реальный YAML Collector, конфиг tail-sampling, строку лога в стиле gctrace и сниппет ручного span — затем выбирай поведение или фикс с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Проблемы OTel диагностируются в YAML, строках лога и коде span — не в слайдах. Прочитай каждый артефакт, предскажи поведение и выбери фикс, который senior platform-инженер сделал бы первым.
Цель
Отработай цикл, который ты гоняешь в каждом инциденте OTel: прочитай конфиг Collector или код span, предскажи, что он делает под нагрузкой, и потянись за изменением, которое реально чинит режим отказа.
Под всплеском трафика этот пайплайн OOM-килит gateway. Что не так и каков фикс?
Heads-up batch и tail_sampling прекрасно сосуществуют — tail_sampling буферизует трейсы, batch группирует сохранённые для экспорта. Дефект в том, что memory_limiter идёт последним, слишком поздно, чтобы защитить от буферизации.
Heads-up Rate-limiting маскирует симптом. Структурный баг — memory_limiter после процессора, потребляющего больше всего памяти; переставь его первым.
Heads-up Второй экспортёр ничего не даёт для входящей памяти буферизации. OOM — в буфере tail_sampling, управляемом порядком процессоров и num_traces, а не fan-out экспорта.
При устойчивых 2000 трейсов/с gateway начинает дропать трейсы до принятия решений, а otelcol_processor_tail_sampling_count_traces_on_memory держится выше num_traces. В чём ошибка сайзинга?
Heads-up Поднятие decision_wait делает хуже: более длинное окно означает больше одновременных трейсов в полёте, так что буфер нужен ещё больше. Фикс — размерить num_traces под rate × window, а не растягивать окно.
Heads-up Процент baseline управляет тем, сколько не-error, не-slow трейсов сохраняется после решения — он не управляет тем, сколько трейсов буферизуется во время ожидания. Давление буфера задаёт peak_rate × decision_wait.
Heads-up Политики OR-ятся: трейс, сохранённый любой политикой, сохраняется один раз. Они не считаются дважды и не вызывают переполнение буфера — это делает недоразмеренный num_traces.
Читая эти четыре self-метрики Collector вместе, каков диагноз?
Heads-up otelcol_exporter_send_failed_spans равно 0, так что backend нормально принимает экспорт. Дропы — от memory_limiter под давлением RAM, проблема ёмкости выше по потоку, а не отказ backend.
Heads-up refused_spans здесь — back-pressure: ресивер отклоняет, потому что цепочка процессоров (memory_limiter) сбрасывает нагрузку у потолка RAM. Конфиг ресивера в порядке; у Collector просто нет запаса памяти.
Heads-up Ненулевые dropped_spans и refused_spans при RSS на 97% лимита — сигнатура недо-провиженного gateway, а не здоровья. Здоровый gateway показывает их в нуле.
У этого ручного span два дефекта, которые senior-ревьюер отмечает сразу. Какие?
Heads-up Установка атрибутов внутри try нормальна и обычна. Реальные дефекты — отсутствующий span.end() (span никогда не флашится) и незаписанное исключение (ошибка не появляется на span).
Heads-up Имя span допустимо и не влияет на корректность sampling. Дефекты — в жизненном цикле: span никогда не завершается и ошибка на нём не записана.
Heads-up await внутри span нормален; контекст OTel распространяется через await благодаря context manager рантайма. Реальные баги — отсутствующий end() и незаписанное исключение.
Итог
Каждый артефакт OTel читается одинаково: в пайплайне Collector порядок процессоров — это корректность (memory_limiter первым или OOM); num_traces у tail-sampling должен быть размерен под peak_rate × decision_wait × safety, а не под baseline; собственные self-метрики Collector отличают gateway, ограниченный памятью (dropped/refused высокие, send_failed ноль), от отказа backend (send_failed высокий); а ручной span обязан end() в finally и записывать исключения, иначе он течёт и прячет те самые ошибки, которые должен был ловить. Читай конфиг, предсказывай режим отказа, чини структурную причину.