- Зачем: - учащийся меняет расчет сессии внутри готового конвейера и проверяет результат внешним эталоном. - Что: - добавлено ТЗ ступени 4 с договором трех SQL-файлов, адресным прогоном и самопроверкой. - индекс лаб ведет на задание; будущими остаются ступени 5–7. - Проверка: - make lint, относительные ссылки и git diff --check прошли. - запрос размещения сессий по шардам проверен на стенде под analyst; данные не изменялись. - выполнены независимое ревью, writing-for-agents, ai-text-lint и литературная вычитка.
10 KiB
Перепиши нарезку стенда
Чему учит: изменить расчетную сущность внутри готового конвейера и проверить результат внешним эталоном.
Сначала прочитать
- задание «Нарежь сессии сам»;
- модель сессии DDS;
- дневной конвейер трансформаций;
- раскладка по шардам.
Граница задания
Стенд уже умеет запускать трансформацию, хранить сессии и проверять их число. Перепишите только три файла:
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. Для каждой строки он должен:
- получить фактическую ноду через
hostName(); - пересчитать ожидаемую ноду по
cityHash64(client_id); - посчитать строки, у которых ноды не совпали.
У кластера два равных шарда: остаток 0 соответствует clickhouse-01, остаток
1 — clickhouse-02. Сгруппируйте ответ по фактической ноде. На каждой ноде
число строк на чужом шарде должно быть нулевым. Счетная DQ-проверка читает
Distributed-таблицу и такую ошибку не заметит.
После зеленого результата запустите etl_pipeline еще раз, не удаляя
партицию. Расчет объема должен пропустить группу сессий: новых прожитых дней и
дыр в слое нет.
Приемка
- переписаны только три файла
sql/dds/session_*.sql, инфраструктура упражнения не изменена; - адресный прогон восстановил выбранный день и завершил
etl_pipelineуспешно; - сохраненные
session_idи деловые колонки десяти строк совпали после пересборки; sessions_vs_referenceдает ноль расхождений во всех прожитых днях;- для одной куки вручную сверены границы и атрибуты сессии;
- пересчет ключа по
hostName()не нашел строк на чужом шарде; - повторный прогон без новой дыры пропустил группу сессий.