Files
clickstream-data-platform/docs/labs/4-1-session-pipeline.md
T
ddadmin 49a156537b docs(labs): добавлено ТЗ переписывания нарезки сессий
- Зачем:
  - учащийся меняет расчет сессии внутри готового конвейера и проверяет результат внешним эталоном.
- Что:
  - добавлено ТЗ ступени 4 с договором трех SQL-файлов, адресным прогоном и самопроверкой.
  - индекс лаб ведет на задание; будущими остаются ступени 5–7.
- Проверка:
  - make lint, относительные ссылки и git diff --check прошли.
  - запрос размещения сессий по шардам проверен на стенде под analyst; данные не изменялись.
  - выполнены независимое ревью, writing-for-agents, ai-text-lint и литературная вычитка.
2026-09-06 11:31:33 -04:00

169 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Перепиши нарезку стенда
**Чему учит:** изменить расчетную сущность внутри готового конвейера и
проверить результат внешним эталоном.
## Сначала прочитать
- [задание «Нарежь сессии сам»](3-1-sessions-by-hand.md);
- [модель сессии DDS](../architecture/dds/session.md);
- [дневной конвейер трансформаций](../architecture/etl.md#объём-прогона);
- [раскладка по шардам](../architecture/storage.md#раскладка-по-шардам).
## Граница задания
Стенд уже умеет запускать трансформацию, хранить сессии и проверять их число.
Перепишите только три файла:
- `sql/dds/session_scope.sql`;
- `sql/dds/session_rebuild.sql`;
- `sql/dds/session_replace.sql`.
Удалите из них готовый SQL и соберите свою реализацию. Даг `dds_transform`
передаст ей параметры и запустит запросы в нужном порядке. DDL уже создает
целевые таблицы и донор. Донор — служебная таблица, где день собирается перед
заменой. Запрос `sessions_vs_reference.sql` проверит опубликованный результат.
## Договор с дагом
Объем прогона — дни, которые соберет один запуск. Его вычисляет первый файл.
| Файл | Вход | Результат |
|---|---|---|
| `session_scope.sql` | `{position:UInt16}` — номер первого несыгранного дня | `first_day` и `last_day` |
| `session_rebuild.sql` | `{first_day:Date}`, `{last_day:Date}`, `{load_id:String}` | Собранные сессии в `dds.session_stage_dist` |
| `session_replace.sql` | День в `{{ params.day }}` | Партиция дня в `dds.session_rep` |
`session_scope.sql` ищет первый прожитый день, которого еще нет в DDS. От него
до последнего прожитого дня получается непрерывный отрезок. Начало шкалы нужно
взять из событий ODS: `position` считает дни от него. Живой модельный день еще
не закончен, поэтому сессии за него не собираются.
Если в ODS нет ни одного события, запрос не возвращает строк. Если DDS уже
догнал мир, запрос возвращает строку с `first_day = NULL`. Даг поймет, что
работы нет, и пропустит группу сессий.
`session_rebuild.sql` сначала очищает `dds.session_stage_rep` на всем кластере.
Затем он собирает в `dds.session_stage_dist` все дни отрезка. Вставка должна
закончиться до замены партиций.
`session_replace.sql` заменяет один день. Цель — локальная таблица
`dds.session_rep`, источник — локальный донор `dds.session_stage_rep`. Операция
выполняется на всем кластере.
`session_scope.sql` читается как один запрос, поэтому в конце ему не нужна
точка с запятой. День для замены подставляет шаблон Jinja в Airflow. Параметр
ClickHouse в `ALTER ... PARTITION` здесь не работает.
## Нарежьте сессии
Источник сборки — `dds.event_v`. Одна строка результата представляет один
визит. Используйте правила, которые уточнили на третьей ступени:
- сессия не пересекает модельные сутки;
- пауза больше 30 минут открывает новую сессию;
- в нарезке участвуют события всех типов;
- при одинаковом времени порядок событий задает `event_id`, поэтому полный
порядок окон — `(event_time, event_id)`;
- `session_id` детерминирован и зависит от куки и начала сессии.
Состав и типы строки заданы в `dds.session_rep`. Заполните начало, конец и
длительность. Посчитайте все события, просмотры, добавления в корзину и
покупки. Возьмите входную и выходную страницы, UTM первого события, устройство
и город. Служебные колонки тоже входят в договор таблицы.
Пишите через `dds.session_stage_dist`. Эта таблица разложит строки по ключу
донора.
## Запустите свою реализацию
Готовые дни повторно не собираются. Чтобы новый SQL точно выполнился, выберите
последний собранный день:
```sql
SELECT max(session_date)
FROM dds.session_v;
```
Сохраните десять строк этого дня без служебных колонок загрузки:
```sql
SELECT * EXCEPT (_load_id, _load_ts)
FROM dds.session_v
WHERE session_date = 'YYYY-MM-DD'
ORDER BY client_id, started_at
LIMIT 10;
```
Подставьте найденную дату и удалите только ее партицию. Команда идет под
служебным пользователем `etl`, потому что `analyst` имеет доступ только на
чтение:
```bash
docker compose exec -T clickhouse-01 sh -c \
'clickhouse-client --user etl --password "$CLICKHOUSE_ETL_PASSWORD" --query "$1"' \
_ "ALTER TABLE dds.session_rep ON CLUSTER clickstream_cluster DROP PARTITION 'YYYY-MM-DD'"
```
Откройте Airflow и запустите `etl_pipeline`. В даге `dds_transform` раскройте
группу `session`: в ней должны выполниться задачи `scope` и `rebuild`, а также
`replace` для выбранного дня. Если после третьей ступени мир ушел вперед,
Airflow создаст `replace` и для последующих прожитых дней. Следом конвейер
запустит витрины и `dq_check`.
Если прогон дошел до замены, но упал на проверке, ошибочная партиция уже
находится в DDS. После правки снова удалите тот же день перед следующим
запуском. Иначе расчет объема сочтет день готовым и пропустит вашу новую
сборку.
## Проверьте результат
Сначала повторите запрос с десятью строками. `session_id` и все деловые
колонки должны совпасть с сохраненным результатом. `_load_id` и `_load_ts`
изменятся: день собрал другой запуск.
Задача `dq_check.sessions_vs_reference` должна стать зеленой. Та же проверка
видна в хранилище:
```sql
SELECT data_date, reference_rows, actual_rows, failed_rows, status
FROM dm.dq_summary_v
WHERE check_name = 'sessions_vs_reference'
ORDER BY data_date;
```
Эта сверка сравнивает число сессий каждой куки за день с `VisitID`. Нулевой
`failed_rows` не подтверждает границы, страницы и UTM: две ошибки могут
оставить тот же счет. Возьмите поштучную сверку из третьей ступени. Для одной
куки выбранного дня сопоставьте границы `VisitID` по событиям с границами
строк `dds.session_v`. Проверьте начало, конец и атрибуты сессии.
Осталась физическая раскладка. Напишите отдельный запрос к
`dds.session_dist`. Для каждой строки он должен:
1. получить фактическую ноду через `hostName()`;
2. пересчитать ожидаемую ноду по `cityHash64(client_id)`;
3. посчитать строки, у которых ноды не совпали.
У кластера два равных шарда: остаток 0 соответствует `clickhouse-01`, остаток
1 — `clickhouse-02`. Сгруппируйте ответ по фактической ноде. На каждой ноде
число строк на чужом шарде должно быть нулевым. Счетная DQ-проверка читает
Distributed-таблицу и такую ошибку не заметит.
После зеленого результата запустите `etl_pipeline` еще раз, не удаляя
партицию. Расчет объема должен пропустить группу сессий: новых прожитых дней и
дыр в слое нет.
## Приемка
- [ ] переписаны только три файла `sql/dds/session_*.sql`, инфраструктура
упражнения не изменена;
- [ ] адресный прогон восстановил выбранный день и завершил
`etl_pipeline` успешно;
- [ ] сохраненные `session_id` и деловые колонки десяти строк совпали после
пересборки;
- [ ] `sessions_vs_reference` дает ноль расхождений во всех прожитых днях;
- [ ] для одной куки вручную сверены границы и атрибуты сессии;
- [ ] пересчет ключа по `hostName()` не нашел строк на чужом шарде;
- [ ] повторный прогон без новой дыры пропустил группу сессий.