Суть Читай реальный код инструментирования, log-строку, PromQL-алерт и конфиг tail-sampling; предскажи поведение observability и выбери фикс с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Баги observability написаны в коде инструментирования и конфигах, а не в дашбордах. Читай сниппет, предскажи, что он сделает со стоимостью или корректностью бэкенда, затем выбери фикс, который senior-инженер делает первым.
Цель
Отработай цикл, который ты прогоняешь в каждом инциденте observability: читай инструментирование, предскажи последствие по cardinality, объёму или семплированию, и тянись к фиксу с наибольшим рычагом, прежде чем кого-то будить.
Этот counter задеплоен в сервис со 100k+ активных клиентов. Что произойдёт и какой фикс сохранит per-customer drill-through?
Heads-up Инкремент бесплатен; стоимость — память на число серий. Каждый отдельный customer_email — это отдельная серия по ~3 КБ в head-блоке — это и есть cardinality-бомба.
Heads-up Email-строки — валидные значения label; '@' не проблема. Проблема — неограниченная cardinality: Prometheus сохраняет серии и исчерпывает память.
Heads-up Scrape interval меняет разрешение сэмплов, а не число активных серий. Память head-блока задаётся числом серий, которое раздувает неограниченный label.
Сниппет 2 — log-строка ретрая
for attempt in range(max_retries): # max_retries только что подняли 3 -> 20 try: return gateway.charge(order) except TimeoutError: logger.info("payment retry", extra={"order_id": order.id, "attempt": attempt})
Викторина
Completed
Трафик ровный, но счёт за логи скакнул после того, как max_retries вырос с 3 до 20. Какое утверждение верно и какой устойчивый фикс?
Heads-up Проблема не в кодировке, а в КОЛИЧЕСТВЕ строк. Plaintext всё равно эмитил бы строку на попытку и был бы труднее для запросов. Эмить одну сводную строку плюс метрику.
Heads-up Число ретраев — бизнес-логика; менять его ради счёта за логи — неверный рычаг. Фикс — дисциплина логов: одна сводная строка на логическую операцию, а не на итерацию.
Heads-up Объём логов следует за эмитируемыми СТРОКАМИ, а не за числом запросов. Цикл, логирующий на итерацию, умножает объём на число итераций даже при ровном трафике.
Сниппет 3 — алерт на cardinality
# выражение алертаrate(prometheus_tsdb_head_series_created_total[5m]) > 1000# triage-запрос, когда сработал:topk(10, count by (__name__) ({__name__=~".+"}))
Викторина
Completed
Что детектит этот алерт и что говорит triage-запрос, когда он срабатывает?
Heads-up head_series_created_total считает СОЗДАНИЕ серий, а не запросы. Запрос считает серии на имя метрики, а не задержку эндпоинта. Это observability cardinality, а не RED-метрики.
Heads-up Он срабатывает на скорости создания серий — предвестнике OOM. Перезапуск проигрывает WAL и перезагружает те же взрывающиеся серии — фикс это metric_relabel_configs labeldrop, затем удаление label в коде.
Heads-up Это метрики TSDB Prometheus про серии, а не ingest логов. Runaway логов детектится по GB/день на сервис в дашборде log-вендора — другой сигнал.
Ревьюер спрашивает: «зачем три политики и какую стоимость мы всё ещё платим, хотя храним только ~2% успешных трейсов?»
Heads-up Probabilistic 2% случайно отбросил бы 98% ошибок и медленных трейсов — именно те, что нужны в инциденте. Политики error и latency существуют, чтобы гарантировать 100%-сохранение этих.
Heads-up Он хранит меньше, но стоит БОЛЬШЕ на коллекторе: каждый span буферизуется ради решения по контексту, поэтому ресурсы коллектора масштабируются с сырым трафиком. Head sampling отбрасывает в начале трейса и не буферизует.
Heads-up Политика status_code ключуется по СТАТУСУ span (статус OTel ERROR), а не по HTTP-коду ответа. 200 с записанным статусом ошибки сохраняется; 4xx, который приложение помечает OK, может не сохраниться.
Итог
Любая проблема observability читается в коде и конфиге: неограниченный label вроде customer_email — это cardinality-бомба (убери его, держи bounded segment плюс exemplar); log-строка на итерацию умножает объём независимо от трафика (эмить одну сводку плюс counter-метрику); алерт на rate head_series_created ловит бомбу до OOM, а topk-запрос называет виновника; конфиг tail-sampling держит 100% ошибок и медленных трейсов, всё ещё платя стоимость коллектора пропорционально сырому трафику. Читай инструментирование, предсказывай ось стоимости, фикси у источника.