Files
clickstream-data-platform/docs/labs/dq-errors-daily.md
T
ddadminandClaude Fable 5 f33d75113d feat(dm): добавлены учебные задания витрин и даг самопроверки
- Зачем:
  - менти строит витрины DM сам, от DDL по правилу зерна до задачи в
    конвейере; стенду нужны ТЗ с образцом и обратная связь по каждой
    витрине (#159, резолюция #135).
- Что:
  - docs/labs/: README и пять ТЗ — events_enriched_v, top_pages_daily,
    session_overview, dq_errors_daily, utm_effectiveness; каждое ТЗ
    называет урок, состав колонок, форму по зерну и самопроверку.
  - dags/homework_check.py: ручной даг, задача на витрину, красная без
    витрины или при расхождении; проверки — шесть файлов sql/dq/ с
    инвариантами против DDS и ODS, без золотых констант; в dq_summary
    не пишет.
  - docs: dm.md называет homework_check и снимает «Остается нарезке»,
    §7 и §10 мастер-спеки согласованы с лабами, dds/session.md указывает
    на ТЗ, AGENTS.md пополнен правилом имен docs/labs/.
- Проверка:
  - make lint; make smoke 21/21, make check-clickhouse 10/10,
    make check-services 7/7.
  - на свежем стенде все пять задач homework_check красные и называют
    ТЗ; черновик каждой витрины по тексту ТЗ зеленит свою задачу,
    после сноса — снова красная; строк от самопроверки в dq_summary нет.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 17:44:15 -04:00

8.3 KiB
Raw Blame History

Задание 4: dm.dq_errors_daily

Чему учит: у брака свой день и свой срок жизни. Витрина качества живет по времени загрузки, а не по модельному дню, и переживает собственный источник — поэтому «пересобрать все» здесь означает «стереть историю».

Роль витрины

Сводка приемки: сколько строк за день не прошли разбор и по какой причине. Читают ее, когда качество потока просело: сначала смотрят, вырос ли класс брака, потом идут за сырым текстом в саму таблицу ошибок.

Витрина отвечает на вопрос «сколько и чего», а не «что именно сломалось». Разбирательство с конкретной строкой начинается в ods.event_errors_dist и ods.order_errors_dist, где рядом с классом лежат сырой текст и координаты доставки.

Источник: единственное исключение из правила _v

Общее правило домашек — читать через публичный договор _v. У таблиц брака такого договора нет, и заводить его ради этой витрины не надо: брак — не сущность бизнес-модели, а диагностика слоя приема. Читать нужно распределенные таблицы напрямую:

  • ods.event_errors_dist — брак разбора событий;
  • ods.order_errors_dist — брак разбора слепка заказов.

Именно _dist, а не _rep: локальная таблица вернет строки одного шарда, и витрина тихо покажет половину брака. Ошибки при этом не будет — ошибка появится позже, когда по витрине примут решение.

Зерно и форма

Зерно — день загрузки × источник × класс брака. Зерно источника — строка брака, значит зерно сменилось и витрина материализуется: dm.dq_errors_daily_rep / _dist, донор _stage_rep / _stage_dist, публичное dm.dq_errors_daily_v. Ключ партиции — день загрузки.

День здесь — toDate(_load_ts), то есть настоящее время, когда строка приехала в хранилище, а не модельный день мира. Модельного дня у брака нет и быть не может: он живет в содержимом, а содержимое как раз и не разобралось.

Состав колонок

Колонка Тип Что это
load_date Date день загрузки, в который приехал брак
source LowCardinality(String) event или order
error_class LowCardinality(String) класс брака из таблицы ошибок
error_rows UInt64 число забракованных строк
_load_id String запуск, собравший день
_load_ts DateTime64(3, 'UTC') когда собран

Две похожие колонки рядом различаются намеренно: load_date — про день, когда приехал брак, _load_ts — про запуск, который собрал витрину. Первая деловая, вторая служебная, и совпадать они не обязаны.

Классы у двух источников свои и не пересекаются по смыслу, поэтому колонка source в зерне обязательна: без нее одинаковое имя класса у событий и у заказов слилось бы в одну строку.

Ритм пересборки: витрина переживает источник

Таблицы брака живут месяц (TTL), сырье — трое суток. Витрина живет без срока: сводка «сколько брака было в марте» нужна и после того, как сами строки марта удалены.

Отсюда правило прогона: пересобираются только те дни загрузки, которые еще есть в источнике. Спроси у источника список живых дней и замени партиции ровно этих дней. Соблазн «собрать все заново в донор и подменить таблицу целиком» здесь стоит дороже обычного: истекшие по TTL дни исчезли бы из витрины навсегда, а вместе с ними — единственная оставшаяся память о них.

Текущий день загрузки не замерзает до конца суток: поток событий идет, и брак в него еще добавится. Это нормально, день просто пересоберется на следующем прогоне.

Ключ шардирования

Твое решение по правилу донора, «Раскладка по шардам». Витрина крошечная: классов брака у событий и заказов вместе меньше десятка. Учти это в защите ключа — иногда правильный ответ звучит как «перекос здесь ничего не стоит, и вот почему».

Как проверить себя

Деловой ключ — пара source и error_class внутри load_date. Область проверки — дни загрузки, которые еще живы хотя бы в одной таблице ошибок: день за границей TTL сверять не с чем, и его отсутствие в источнике не ошибка.

Собери прямой счет по обеим таблицам ошибок, соедини с dm.dq_errors_daily_v полным внешним соединением по ключу и посчитай строки, где ключа нет с одной из сторон или где расходится error_rows. Ноль — зеленый.

Отдельно: сумма error_rows по живым дням равна общему числу строк в ods.event_errors_dist плюс ods.order_errors_dist. Если эта сумма вышла примерно вдвое меньше — витрина почти наверняка собрана с _rep вместо _dist и видит один шард из двух.

Пустая витрина — верный ответ. На чистом стенде брака нет, обе стороны пусты, расхождений ноль, задача зеленая. Проверка, которая требует хотя бы одной строки, на здоровом стенде краснела бы всегда и приучала бы себя игнорировать.

Приемка

  • пять объектов витрины созданы, донор объявлен через AS и шардируется ключом цели;
  • источники — _dist-таблицы обоих видов брака;
  • прогон заменяет партиции только живых дней источника и не трогает истекшие;
  • задача dq_errors_daily в homework_check зеленая — в том числе на чистом стенде, где брака нет;
  • задача витрины добавлена в dm_transform.