Files
clickstream-data-platform/docs/labs
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
..

Учебные задания: витрины слоя DM

Пять домашек. Стенд привозит слой DM наполовину: витрины трех сюжетов дашборда и контроль качества построены, остальные витрины строит менти. Каждое задание — самостоятельное ТЗ: что за витрина, какое у нее зерно, из чего она собирается и как проверить, что собралась верно.

Общее устройство слоя — дока DM. Она отвечает на вопросы «почему так», задание отвечает на вопрос «что сделать».

Порядок

Задания идут по нарастанию: каждое следующее опирается на то, что уже усвоено.

№ Витрина Чему учит
1 events_enriched_v слой — это контракт, а не копия данных
2 top_pages_daily зерно сменилось — витрина материализуется и встает в конвейер
3 session_overview зерно решает, на какие вопросы витрина уже не ответит
4 dq_errors_daily у брака свой день и свой срок жизни
5 utm_effectiveness declared_ в имени — предупреждение, а не украшение

Общие правила

Они одни на все пять заданий, поэтому записаны здесь, а не повторяются в каждом ТЗ.

Читать — через _v. Публичный договор сущности DDS — представление с суффиксом _v: dds.event_v, dds.session_v. Витрина называет только его; физические _rep и _dist служат загрузке и обслуживанию. Соседнюю домашку витрина источником тоже не берет: витрина слоя DM читает договор DDS, и задания остаются независимыми. Исключение одно и оно оговорено в задании 4: у таблиц брака публичного договора нет.

Писать — только в _dist. При записи в локальную таблицу раскладку определяет то, какая нода случайно выполняла код, а не свойство данных. Правило и его следствия — дока хранилища, «Раскладка по шардам».

Форма — по зерну. Витрина, меняющая зерно источника, материализуется: dm.<имя>_rep / _dist, донор dm.<имя>_stage_rep / _stage_dist и публичное dm.<имя>_v с явным списком колонок. Витрина, сохраняющая зерно, остается обычным представлением. Довод — дока DM, «Форма: по зерну, не по цене запроса».

Ключ шардирования — твое решение. Ни одно ТЗ ключа не называет. Правило выбора и требование к донору — дока хранилища, «Раскладка по шардам»: донор объявляется через AS <цель>_rep и шардируется тем же ключом, что цель, иначе замена партиции потеряет строку на одном шарде и задвоит на другом. Названный ключ надо уметь защитить: почему он не дает перекоса и почему более сложный тут ничего не добавляет. Образец такой защиты — dm.revenue_daily_dist в sql/ddl/55-dm-tables.sql.

Где лежит код. DDL домашек — свой файл sql/ddl/70-labs.sql: он применяется после стендовых по порядку имени, и твои объекты не мешаются со стендовыми. У агрегатов рядом со стендовыми ложатся еще три запроса — sql/dm/<витрина>_scope.sql спрашивает у хранилища, какие дни пересобирает запуск, _rebuild.sql собирает их в донор, _replace.sql заменяет партицию, — и задача в dags/dm_transform.py. У разминочного представления нет ничего из этого: весь его код — одно CREATE VIEW.

Образец на все случаи — витрина revenue_daily: у нее есть все пять объектов, донор, сборка, замена дневных партиций и задача в конвейере. Списывать с нее устройство можно и нужно; списывать состав колонок — нет, он у каждой витрины свой.

Самопроверка

Даг homework_check запускается рукой, расписания у него нет. Задача витрины красная, пока витрины нет или счет не сходится, и зеленая, когда собрано верно. В сводку качества конвейера dm.dq_summary он не пишет: это инструмент лабы, а не история качества стенда.

Проверка у всех заданий устроена одинаково и золотых констант не содержит: рядом с витриной независимым запросом собирается тот же ответ — прямо из источника витрины, минуя ее саму. У четырех заданий источник эталона тот же, что у витрины, dds.session_v или dds.event_v; у задания 4 это таблицы брака ODS, потому что в DDS у брака представительства нет. Обе стороны соединяются по деловому ключу через FULL OUTER JOIN, и считается число строк, где стороны разошлись или одной стороны нет. Ноль — зеленый. Полное соединение здесь обязательно: при обычном пропавший день или пропавший ключ просто не попал бы в результат и остался бы незамечен.

Раздел «Как проверить себя» каждого ТЗ называет деловой ключ и сравниваемые величины. Тот же прием в стендовом исполнении — даг dq_check и запросы в sql/dq/.

Предупреждение. Запросы самой проверки лежат в sql/dq/ открыто, и имена называют витрину и роль сверки. Проверка на инвариантах неизбежно близка к ответу: чтобы сверить витрину, надо ее пересчитать. Прятать эти файлы никто не станет — но открыв их до того, как задание сделано, ты обменяешь урок на готовый SQL. Смотреть туда стоит после своей сборки: сравнить два решения полезно, списать одно — нет.

Уборка

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