- Зачем:
- README и путеводитель обращаются к читателю на «вы», а ТЗ в docs/labs/
были на «ты»; после пересборки индекса смесь стала заметна.
- Что:
- во всех восьми файлах docs/labs/ местоимения, глаголы и повелительное
наклонение переведены во второе лицо множественного числа.
- названия ступеней в таблице README оставлены как в спеке.
- Проверка:
- grep по формам «ты/твой», 'ё' и 'менти' по docs/labs/ молчит;
git diff --check чистый; ревью диффа свежей сессией.
122 lines
9.2 KiB
Markdown
122 lines
9.2 KiB
Markdown
# Задание 3: `dm.session_overview`
|
||
|
||
**Чему учит:** агрегат хранит слагаемые, а не готовые средние и доли.
|
||
Среднее по строкам витрины не складывается никогда, а уникальных нельзя
|
||
складывать даже тогда, когда на сегодняшних данных сумма сходится.
|
||
|
||
## Роль витрины
|
||
|
||
Дневной портрет визитов в разрезе устройств: сколько было сессий, какие они
|
||
были длинные, сколько страниц смотрели и как часто доходили до покупки.
|
||
Вопрос витрины — «как люди ходят по магазину с телефона и с настольного
|
||
компьютера, и чем эти визиты отличаются».
|
||
|
||
Сессия на стенде — расчетная сущность, ее уже нарезал слой DDS. Собирать
|
||
визиты заново из событий не нужно и нельзя: это была бы вторая нарезка,
|
||
которая тихо разойдется с первой.
|
||
|
||
Витрина с таким именем была и на прошлом стенде, но зерно у нее сменилось,
|
||
и сменилось не по вкусу. Там визитов как сущности не было, и витрина
|
||
нарезала их сама, строкой на визит. Здесь визит — сущность слоя DDS, и
|
||
витрина, повторяющая его зерно, оказалась бы обычным представлением по
|
||
[правилу формы](../architecture/dm.md): копией сессий с другими именами
|
||
колонок. Чтобы задание осталось про дневной агрегат, витрина поднялась на
|
||
зерно выше — до дня и среза.
|
||
|
||
## Зерно и форма
|
||
|
||
Зерно — **день сессии × категория устройства**. Источник —
|
||
`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`.
|
||
|
||
Это не отменяет правила, а показывает, откуда берется совпадение: в мире
|
||
стенда устройство приписано куке на всю жизнь ([словарь
|
||
понятий](../../CONTEXT.md), «Паспорт куки»), поэтому одна кука лежит ровно
|
||
в одной строке дня, и пересекаться строкам не в чем.
|
||
|
||
Совпадение это — свойство мира, а не свойство витрины. Появятся визиты
|
||
одной куки с двух устройств — сумма поедет, и ни одна проверка об этом не
|
||
скажет: отчет просто начнет тихо завышать аудиторию. Отсюда правило,
|
||
которое стоит унести с задания: складывать уникальных нельзя даже там, где
|
||
сегодня получается. Число посетителей дня спрашивают у
|
||
`dm.daily_traffic_v`, где оно посчитано на своем зерне.
|
||
|
||
## Ритм пересборки
|
||
|
||
Источник — сессии: день замерзает, как только прожит. Объем прогона тот же,
|
||
что в задании 2, — хвост от первого несобранного прожитого дня; механика та
|
||
же: донор и замена дневных партиций.
|
||
|
||
## Ключ шардирования
|
||
|
||
Ваше решение по [правилу донора](../architecture/storage.md), «Раскладка по
|
||
шардам». Здесь у измерения всего три встречающихся значения, и распределены
|
||
они неровно: планшеты — тонкая полоса, а телефоны и настольные машины держат
|
||
основную массу. Прежде чем брать хеш от одного `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`.
|