- Зачем:
- менти строит витрины 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>
115 lines
7.7 KiB
Markdown
115 lines
7.7 KiB
Markdown
# Задание 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` **нет** — и не должно быть.
|