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

10 KiB
Raw Blame History

Перепиши нарезку стенда

Чему учит: изменить расчетную сущность внутри готового конвейера и проверить результат внешним эталоном.

Сначала прочитать

Граница задания

Стенд уже умеет запускать трансформацию, хранить сессии и проверять их число. Перепишите только три файла:

  • 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 точно выполнился, выберите последний собранный день:

SELECT max(session_date)
FROM dds.session_v;

Сохраните десять строк этого дня без служебных колонок загрузки:

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 имеет доступ только на чтение:

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 должна стать зеленой. Та же проверка видна в хранилище:

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() не нашел строк на чужом шарде;
  • повторный прогон без новой дыры пропустил группу сессий.