# Дашборд «Данные» `infra/grafana/provisioning/dashboards/data.json` — шесть панелей на один экран. Вопрос у экрана один: с потоком всё в порядке? Состав панелей и довод за каждую — [ADR 0002](../../adr/0002-monitoring-scope.md). Свой вопрос каждая панель называет заголовком прямо на экране. Здесь — только источники, о них и речь ниже. | Панель | Откуда берёт | |---|---| | Свежесть | `ods.event_dist`, `ods.order_dist` | | Поток | `stg.hits_raw_dist`, `stg.orders_raw_dist` | | Брак | те же и `ods.*_errors_dist` | | Сходимость | экспортёр Kafka и `stg.hits_raw_dist` | | Отставание | `kafka_consumergroup_lag` | | Дневной слепок | `stg.orders_raw_dist` | ## Почему слои у панелей разные Свежесть спрашивают у ODS. Это слой, который читают, и у заказов дорога до него длиннее: в STG данные привозит забор, а типизированную строку кладёт разбор. Встань разбор — свежесть ODS это покажет, а свежесть STG — нет. Читает свежесть физическую `_dist`, а не каноническое `ods.*_v`. Представление прячет за собой `FINAL` и отвечает про текущее состояние сущностей, а вопрос здесь про доставку. Местом диагностики доставленных версий физическая таблица названа там же, где заведено представление (`sql/ddl/30-ods-views.sql`). Поток и брак считаются по сырому слою. Он хранит каждую доставку и ничего не схлопывает, а `ReplacingMergeTree` в ODS схлопнет и повтор доставки события, и все версии одного заказа. Считай мы поток по ODS — он занизился бы, и тем сильнее, чем дольше живёт стенд. Доля брака берётся от приехавшего, а не от суммы «годные плюс брак»: брак не отдельная куча, а часть сырых данных, которая не прошла разбор. Окно у панели брака — окно дашборда, и это не косметика. Сырые данные живут трое суток, а брак — месяц: доля за всё время делила бы месяц брака на трое суток приехавшего и однажды перевалила бы за сотню процентов. Панель дневного слепка, наоборот, окна не знает. Её вопрос про состояние, а не про отрезок, и последние десять слепков она показывает всегда. Отдельного ряда «против вчерашнего» на панели потока нет, и это решение, а не пропуск. Реальное «вчера» на стенде чаще всего приходится на время, когда стенда ещё не было. А сдвинуть ряд на модельные сутки значит записать в JSON дашборда ускорение мира — ровно ту пометку про тик, которую [ADR 0002](../../adr/0002-monitoring-scope.md) в дашборде держать не велит: поменяют ускорение, и панель молча соврёт. Сравнение остаётся глазу: модельные сутки укладываются в минуты реального времени, и соседние дни мира помещаются в одно окно панели сами. ## Приземлившееся считают в сыром слое Приземлившееся панели считают по `stg.*_raw_dist`, произведённое — по метрикам экспортёра. Таблицы движка Kafka `stg.hits_raw_kafka` и `stg.orders_raw_kafka` хранилищем не работают: у второго стоит `kafka_commit_on_select` (`sql/ddl/10-stg-tables.sql`), и запрос из панели забрал бы слепок себе, сдвинул офсеты, а даг приёма приехал бы к пустому топику. ## Как считается сходимость Произведённое живёт в Kafka, приземлившееся — в ClickHouse, и со своей стороны ни одна из них не видит другую. Поэтому панель смешанная: ряд «произведено в Kafka» приходит из Prometheus, ряд «приземлилось в ClickHouse» — из ClickHouse. Единицы общие: сообщения за интервал панели. Со стороны Kafka это разность офсетов топика, «сейчас минус интервал назад», с суммой по обеим партициям; со стороны ClickHouse — `count()` строк STG по тому же интервалу. Два соседних способа отвергнуты, и оба по замеру на живом стенде. `increase` растягивает приращение на всё окно и на минутном интервале промахивается на проценты: здоровый стенд выглядел бы расходящимся. Накопленный итог от начала окна точнее обоих приростов, но отсчитывать его надо от края окна — а на свежем стенде окно начинается раньше, чем появился экспортёр, и ряд пропадает целиком. Столбцы ClickHouse подписаны правым краем, а не левым. Точка Prometheus — это прирост к моменту времени, а `count()` по интервалу — сумма, которая набирается к его концу; подпиши мы её началом, здоровые ряды разъехались бы ровно на один столбец. Шаг панели при этом не мельче минуты: экспортёр опрашивают раз в пятнадцать секунд, и на более мелком шаге ряд Kafka пошёл бы ступеньками там, где ряд ClickHouse идёт гладко. Точка в точку ряды всё равно не совпадут, и ждать этого не надо: опрос экспортёра не попадает на границы столбцов, поэтому на разгоне потока линия Kafka идёт чуть выше. Читать надо устойчивый разрыв между линиями; разница в один столбец не значит ничего. Панель смотрит на топик событий. У него приём непрерывный, и разрыв виден сразу. Слепок заказов уезжает пачкой раз в модельные сутки, и вопрос к нему другой — не «сходится ли поток», а «доехала ли выгрузка и вовремя ли». На него отвечает своя панель, и модельный день она берёт из самого сообщения: в STG он приезжает полем `snapshot_date`. ## Что проверено Дашборд проверен на живом стенде 21 августа 2026 года, `world_live` снят с паузы. - Дашборд поднимается из каталога сам: файл положен, панели появились без ручного импорта. Сохранение и удаление через API отвергнуты («Cannot save provisioned dashboard», «provisioned dashboard cannot be deleted»). - Все шесть панелей отвечают через `/api/ds/query` — и в окне часа по умолчанию, и в окне на шесть часов, которое начинается раньше, чем поднялся экспортёр. Пустых нет ни при идущем потоке, ни при остановленном. - Брак: подсунутое в топик `hits` негодное сообщение легло в `ods.event_errors` с классом `keyset_mismatch`; панель показала единицу и долю 0,039 % в десятиминутном окне. Строка убрана мутациями из `stg.hits_raw_rep` и `ods.event_errors_rep`, обе таблицы вернулись к нулю. - Сходимость: на живом потоке за двадцать пять минут медиана расхождения между рядами 3 %, худший столбец 11 % — на переломе потока, как и описано выше. - Свежесть реагирует на остановленный поток и возвращается. Через пять минут после конца модельного дня панель показала 305 с у событий и 288 с у заказов; стоило пустить следующий день, события вернулись к секунде. Свежесть заказов при этом растёт до конца дня и садится, когда доедет его слепок, — так и устроен второй источник.