- Зачем:
- менти строит витрины 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>
7.7 KiB
Задание 1: dm.events_enriched_v
Чему учит: слой — это контракт, а не копия данных. Витрина, которая не меняет зерно источника, остается представлением, и копии событий ради других имен колонок не заводит ни один боевой склад.
Это разминка. Строк она не хранит, в конвейер не встает, ключа шардирования у нее нет. Задача одна: собрать плоское событие для BI и не поддаться соблазну разложить его на диск.
Роль витрины
Договор события в DDS широкий — 44 колонки, почти весь формат выгрузки: часть из них технические координаты источника, часть — параллельные массивы товаров. Аналитику в Superset нужен плоский набор деловых полей и пара производных, которые иначе каждый пишет заново в каждом запросе.
Витрина отвечает на вопрос «что вообще происходило»: с нее начинают, когда надо посмотреть на события глазами человека, а не разбирать широкую строку источника.
Источником для витрин следующих заданий она не служит. Витрина слоя DM читает публичный договор DDS — так устроены и стендовые витрины, и потому задания независимы: соседнюю домашку можно делать первой, а можно не делать вовсе.
Зерно и форма
Зерно — событие: одна строка витрины равна одной строке dds.event_v.
Зерно источника сохранено, значит форма — обычное представление
CREATE VIEW dm.events_enriched_v, без физической таблицы, без донора и
без задачи в dm_transform. Пересобирать нечего.
Источник — dds.event_v, и только он.
Состав колонок
Явный список обязателен: договор не должен расширяться сам, когда в источник добавят колонку.
Время и посетитель: event_date, event_time, event_hour,
client_id, event_id.
Действие: event_type (pageview, add_to_cart, purchase), url,
page_path, referer, referer_host, title.
Атрибуция: utm_source, utm_medium, utm_campaign, utm_content,
utm_term.
Паспорт визита: device_category, browser, operating_system,
region_country, region_city.
Торговый блок, массивами как в источнике: product_id, product_name,
product_category, product_price, product_quantity,
product_event_type, purchase_id, purchase_revenue, purchase_coupon.
Три колонки в источнике не лежат и считаются здесь:
| Колонка | Как считается | Зачем |
|---|---|---|
event_hour |
toHour(event_time) |
суточный профиль спрашивают в каждом отчете |
page_path |
путь url без домена и параметров |
группировка по странице без разбора строки в каждом запросе |
referer_host |
домен referer |
«откуда пришли» одним значением, а не полным адресом |
Служебных _load_id и _load_ts у витрины нет, и это не забывчивость. Их
нет и у dds.event_v: событие в DDS не грузят, а читают из ODS, поэтому
координат собственной загрузки у него не бывает. Служебная колонка называет
запуск, который положил строку на диск, — здесь на диск никто ничего не
кладет.
Ловушка задания
Массивы product_* тянет развернуть через ARRAY JOIN — так плоская
таблица становится по-настоящему плоской. Разворачивать нельзя, и вреда
здесь два.
Очевидный: у события с тремя позициями получилось бы три строки, зерно витрины перестало бы совпадать с зерном источника, а вместе с ним развалилось бы и правило формы — такую витрину уже пришлось бы материализовать.
Неочевидный и куда более разрушительный: ARRAY JOIN — соединение
внутреннее, и строку с пустым массивом он выбрасывает. Торговый блок пуст у
всех событий, кроме корзин и покупок, то есть у подавляющего большинства.
Так «плоская таблица событий» превращается в таблицу товарных позиций и
теряет девятнадцать строк из двадцати — а по имени и по составу колонок она
по-прежнему выглядит таблицей событий. Ошибка не падает и ничего не
подсвечивает.
Массивы едут в договор как есть; разворачивает их тот, кому нужна позиция заказа, — и делает это уже в своей витрине, с другим зерном и другим именем.
Как проверить себя
Деловой ключ — event_id внутри дня. Собери прямой ответ из dds.event_v
за тот же день, соедини с витриной полным внешним соединением и посчитай
строки, где ключа нет с одной из сторон или где различаются значения
деловых колонок. Ноль — зеленый.
Отдельно проверяются три производные колонки: event_hour, page_path и
referer_host должны совпасть с тем же выражением, посчитанным по
dds.event_v на лету.
Если строк в витрине оказалось в разы меньше, чем в источнике, а остались в
ней одни корзины и покупки — это развернутые массивы: ARRAY JOIN выбросил
все события с пустым торговым блоком.
Приемка
dm.events_enriched_vсоздано, физических таблиц под ним нет;- список колонок в представлении явный;
- число строк и набор
event_idсовпадают сdds.event_vза каждый прожитый день; - задача
events_enrichedвhomework_checkзеленая; - задачи в
dm_transformнет — и не должно быть.