- Зачем:
- менти строит витрины 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>
9.1 KiB
Задание 3: dm.session_overview
Чему учит: агрегат хранит слагаемые, а не готовые средние и доли. Среднее по строкам витрины не складывается никогда, а уникальных нельзя складывать даже тогда, когда на сегодняшних данных сумма сходится.
Роль витрины
Дневной портрет визитов в разрезе устройств: сколько было сессий, какие они были длинные, сколько страниц смотрели и как часто доходили до покупки. Вопрос витрины — «как люди ходят по магазину с телефона и с настольного компьютера, и чем эти визиты отличаются».
Сессия на стенде — расчетная сущность, ее уже нарезал слой DDS. Собирать визиты заново из событий не нужно и нельзя: это была бы вторая нарезка, которая тихо разойдется с первой.
Витрина с таким именем была и на прошлом стенде, но зерно у нее сменилось, и сменилось не по вкусу. Там визитов как сущности не было, и витрина нарезала их сама, строкой на визит. Здесь визит — сущность слоя DDS, и витрина, повторяющая его зерно, оказалась бы обычным представлением по правилу формы: копией сессий с другими именами колонок. Чтобы задание осталось про дневной агрегат, витрина поднялась на зерно выше — до дня и среза.
Зерно и форма
Зерно — день сессии × категория устройства. Источник —
dds.session_v, у него зерно визита, значит зерно сменилось и витрина
материализуется: dm.session_overview_rep / _dist, донор _stage_rep /
_stage_dist, публичное dm.session_overview_v. Ключ партиции — день.
Категория устройства взята из сессии как есть: у куки она постоянна, и слой
DDS уже перевел код источника в слово. Словарь расшифровки шире, чем мир
стенда: слов в нем четыре, а встречаются три — desktop, mobile и
tablet. Строку под tv заводить незачем, она возникнет сама, если такие
визиты появятся.
Состав колонок
| Колонка | Тип | Что это |
|---|---|---|
report_date |
Date |
день сессии |
device_category |
LowCardinality(String) |
категория устройства |
sessions |
UInt64 |
число визитов |
visitors |
UInt64 |
число разных client_id |
bounce_sessions |
UInt64 |
визиты ровно из одного события |
purchase_sessions |
UInt64 |
визиты, в которых была хотя бы одна покупка |
duration_seconds_total |
UInt64 |
суммарная длительность визитов |
events_total |
UInt64 |
событий во всех визитах |
pageviews |
UInt64 |
просмотров страниц |
cart_adds |
UInt64 |
добавлений в корзину |
purchases |
UInt64 |
покупок |
_load_id |
String |
запуск, собравший день |
_load_ts |
DateTime64(3, 'UTC') |
когда собран |
Средней длительности и доли отказов в витрине нет намеренно. Хранить
avg_duration_seconds было бы удобнее ровно один раз — пока смотрят одну
строку. Тот, кто захочет среднее по дню целиком, сложит средние по трем
устройствам и получит неправду: средние не складываются, они взвешиваются.
Пара «duration_seconds_total и sessions» дает верный ответ на любом
срезе, а деление стоит потребителю один вызов. То же и с долей отказов:
храним bounce_sessions и sessions, долю считает тот, кто спрашивает.
С visitors история тоньше, и в ней главная ловушка задания. Складывать
уникальных по строкам нельзя: один и тот же посетитель, попавший в две
строки, будет посчитан дважды. Проверь на стенде — и увидишь, что сумма
visitors по устройствам за день сходится с dm.daily_traffic_v.
Это не отменяет правила, а показывает, откуда берется совпадение: в мире стенда устройство приписано куке на всю жизнь (словарь понятий, «Паспорт куки»), поэтому одна кука лежит ровно в одной строке дня, и пересекаться строкам не в чем.
Совпадение это — свойство мира, а не свойство витрины. Появятся визиты
одной куки с двух устройств — сумма поедет, и ни одна проверка об этом не
скажет: отчет просто начнет тихо завышать аудиторию. Отсюда правило,
которое стоит унести с задания: складывать уникальных нельзя даже там, где
сегодня получается. Число посетителей дня спрашивают у
dm.daily_traffic_v, где оно посчитано на своем зерне.
Ритм пересборки
Источник — сессии: день замерзает, как только прожит. Объем прогона тот же, что в задании 2, — хвост от первого несобранного прожитого дня; механика та же: донор и замена дневных партиций.
Ключ шардирования
Твое решение по правилу донора, «Раскладка по
шардам». Здесь у измерения всего три встречающихся значения, и распределены
они неровно: планшеты — тонкая полоса, а телефоны и настольные машины держат
основную массу. Прежде чем брать хеш от одного device_category, посчитай,
сколько строк ляжет на каждый шард, и назови, чем тебя устраивает результат.
Как проверить себя
Деловой ключ — device_category внутри дня. Собери прямой агрегат из
dds.session_v за день, соедини с dm.session_overview_v полным внешним
соединением и посчитай строки, где ключа нет с одной из сторон или где
расходится любой счетчик. Ноль — зеленый.
Две проверки покрупнее ловят грубую ошибку сразу:
- сумма
sessionsза день равна числу строкdds.session_vэтого дня: ни один визит не потерян и ни один не задвоен; - сумма
events_totalза день равна суммеevents_totalпо сессиям того же дня.
Если суммы событий сходятся, а сумма сессий больше числа визитов — почти наверняка витрина собрана из событий, а не из сессий.
Приемка
- пять объектов витрины созданы, донор объявлен через
ASи шардируется ключом цели; dm.session_overview_vперечисляет колонки явно;- источник витрины —
dds.session_v, своей нарезки визитов в ней нет; - средних и долей в физической таблице нет;
- задача
session_overviewвhomework_checkзеленая; - задача витрины добавлена в
dm_transform.