Files
clickstream-ch-kafka-supers…/docs/course/LEARNING_PLAN.md
T
ddadminandClaude Opus 4.8 1dd79a3287 docs(course): лабы 07 (next-day) и 08 (continue) + метадокументы (#22)
Зачем: два новых режима роста мира не имели уроков, а метадокументы
курса не знали о новом маршруте.

Что: лаба 07 «Следующий день и границы времени» — инкремент дня,
сверка manifest, переходящие визиты запросом в dds.event, включение
расписания на один запуск, цена роста full_refresh; лаба 08 «Живой
поток и свежесть данных» — расслоение свежести слоёв, стоп/продолжение
генератора, users < sessions, врезка про модельное время; LEARNING_PLAN
и README курса согласованы с маршрутом 0–8; в CONTEXT.md починена
ссылка «урок 7» (теперь ведёт на врезку лабы 08). Числа со стенда
помечены маркером «сверить-на-стенде».

Проверка: обе лабы по шаблону LESSON_STANDARD (6 секций, явный «верни
как было»); внутренние ссылки разрешаются; git diff --check чистый.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 13:36:30 +03:00

15 KiB
Raw Blame History

План обучения: курс «Кликстрим на ClickHouse»

Дата: 2026-06-03 (аудит путей выполнен; середина расщеплена — один паттерн на урок, см. §1–2). Поправка 2026-07-23: весь маршрут стал обязательным. После урока 6 добавлены лабы 07–08 про следующий день и живое продолжение; обе лабы написаны. Назначение: высокоуровневый маршрут менти по курсу — карта уроков, порядок, результаты аудита эталонных путей. Рамка курса (зачем/что/скоуп) — в PRD.md; как устроен отдельный урок — в LESSON_STANDARD.md.

Раздел 3 содержит финальные вердикты аудита и конкретный список правок — их применяем при написании соответствующих уроков, а не отдельным забегом.


1. Маршрут

Порядок линейный, все уроки и лабы обязательны. Урок 0 — разминка, а уроки 1→4 повторяют сам пайплайн (STG → ODS → DDS → оркестрация). Мониторинг (5) и Superset (6) работают как надстройки поверх готовых данных. Лабы 07–08 затем растят импортированный мир: сначала детерминированным следующим днём, потом живым продолжением.

Принцип нарезки — один прод-паттерн на урок (LESSON_STANDARD §2). Поэтому середина пайплайна разнесена: типизация+DQ (ODS) и сборка сущностей (DDS) — разные уроки. Витрины DM не отдельный урок: их показываем в деле там, где их потребляют — в мониторинге (DQ-витрины) и BI (traffic/utm-витрины).

Что нужно знать заранее по Kafka: перед уроком 0 менти смотрит обзорное видео по Kafka из роадмапа — оно даёт словарь терминов. Урок 0 связывает этот словарь с тем, что менти сразу видит в живом стенде.

2. Карта уроков

# Урок Эталонный путь (файлы стенда) Статус Режим
0 Вводный урок по Kafka Kafka UI: топики, партиции, offset'ы, consumer-группы, отставание (lag) обязательный наблюдение
1 Заземление Kafka → ClickHouse sql/ddl/stg/10_stg.sql (Kafka engine → MV → MergeTree) обязательный руки
2 STG → ODS: типизация и DQ-split sql/ods/20_stg_to_ods.sql + DDL sql/ddl/ods/20_ods.sql обязательный руки
3 ODS → DDS: сборка сущностей sql/dds/30_ods_to_dds.sql + DDL sql/ddl/dds/30_dds.sql (argMax, UNION, сироты) обязательный руки
4 Оркестрация в Airflow airflow/dags/etl_pipeline_dag.py (зависимости, гейты, остановка при нарушениях) обязательный руки
5 Мониторинг Prometheus + Grafana + экспортёры обязательный наблюдение
6 BI-витрина Superset поверх ClickHouse обязательный руки
7 Лаба: следующий день airflow/dags/world_next_day_dag.py написана, обязательная руки
8 Лаба: живое продолжение make generator-continue и etl_pipeline написана, обязательная руки

Урок 0 — обязательная разминка без правок кода. В уроках 1–6 и лабах 07–08 менти запускает стенд или делает управляемую правку; каждый такой эксперимент заканчивается явным возвратом к чистому состоянию.

Где «MV vs батч»: контраст из цели №2 PRD — это мост уроков 1→2. Урок 1 (STG) заканчивается вопросом «мы приземлили поток через MV — почему дальше не MV?»; урок 2 (ODS) отвечает: батч ради наблюдаемости и управляемости пересчёта. Отдельным уроком контраст не выделяем — он живёт на стыке.

Витрины DM показываем в деле в уроках 5–6; единственную концепцию DM («VIEW сейчас, материализуем если затормозит») даём короткой заметкой в финале урока 3.

3. Аудит эталонных путей

Вердикт по каждому пути — один из трёх: годно как есть / точечно править / переписать (критерии «учебного качества» — в LESSON_STANDARD.md).

Финальная оценка (детальный аудит от 2026-06-03):

Урок Путь Файлы Вердикт
1 STG (Kafka→CH) sql/ddl/stg/10_stg.sql точечно править (баг конвертации kafka_ts, найден на стенде)
2 STG→ODS sql/ods/20_stg_to_ods.sql + DDL sql/ddl/ods/20_ods.sql точечно править
3 ODS→DDS (+ демоут DM) sql/dds/30_ods_to_dds.sql + DDL sql/ddl/dds/30_dds.sql; DM sql/dm/40_dds_to_dm.sql + sql/ddl/dm/40_dm.sql точечно править
4 Airflow DAG airflow/dags/etl_pipeline_dag.py точечно править
5 Мониторинг configs/*, экспортёры годно как есть (для режима наблюдения)
7 Следующий день airflow/dags/world_next_day_dag.py точечно править

Карты путей раздела 2 включают и DDL целевых таблиц (sql/ddl/{ods,dds}/*), а не только батч-трансформации: без формы целевых таблиц слой читается неполно.

3.1. Список правок (применяем при написании урока)

Правки точечные, делаем не отдельным забегом, а в составе соответствующего урока.

Урок 1 — STG (sql/ddl/stg/10_stg.sql):

  • Баг конвертации kafka_ts (найден прогоном на стенде 2026-06-03). Было fromUnixTimestamp64Milli(toInt64(_timestamp_ms)) во всех 4 MV: _timestamp_ms это DateTime64(3), toInt64() срезает его до секунд, и fromUnixTimestamp64Milli читает секунды как миллисекунды → kafka_ts = 1970-01-21 по всему стенду. Исправлено на _timestamp_ms AS kafka_ts (прямое присвоение, мс сохраняются). Прежний аудит ошибочно пометил путь «годно как есть» — потому что его не прогоняли.
  • Колоночные комментарии-пересказы (-- Партиция, -- Имя топика) безвредны — не трогаем.
  • В финале урока — мост к уроку 2: вопрос «почему дальше не MV?».

Урок 2 — STG→ODS, типизация + DQ-split (sql/ods/20_stg_to_ods.sql + DDL sql/ddl/ods/20_ods.sql):

  • Кандидат на war-story по типам: баг kafka_ts из урока 1 (toInt64(DateTime64) молча срезал миллисекунды → 1970). Это идеальная иллюстрация темы урока — «тихая потеря данных на неверном типе». Решено: дан как пример в уроке 2 (war-story в финале).
  • Добавить однострочный «поток данных» в шапку 20_stg_to_ods.sql (stg.*_raw → ods.* + ods.*_errors) — п.3 чеклиста.
  • Добавить строку «зачем»: основная таблица = валидный ключ (но может иметь parse_errors по неключевым полям), а *_errors = любая ошибка. Без этого непонятно, почему одна строка попадает в оба места (двойной учёт) — проваливается тест одного прохода.
  • Убрать мусор: в sql/ddl/ods/20_ods.sql (стр. ~15–25) — восемь DROP TABLE IF EXISTS stg.mv_*_to_ods (чистка legacy-MV). Шум прошлой архитектуры, мента читает его раньше сути. Вынести из учебного файла.
  • Минор: в 20_ods.sql разнобой партиционирования (browser по event_date, остальные по src_ingest_ts) — одна строка «почему» либо унифицировать.
  • В «Зачем» урока — ответ на мост из урока 1: батч ради наблюдаемости/управляемости.

Урок 3 — ODS→DDS, сборка сущностей (sql/dds/30_ods_to_dds.sql + DDL sql/ddl/dds/30_dds.sql):

  • Добавить однострочный «поток данных» в шапку 30_ods_to_dds.sql (ods.* → dds.click + dds.event) — п.3 чеклиста.
  • В финале урока — recap всей цепочки STG→ODS→DDS: защита от фрагментации после расщепления середины (менти должен собрать сквозную модель, а не три изолированных слоя).
  • Демоут DM оформляем здесь: убрать мусор в sql/dm/40_dds_to_dm.sql (стр. ~14–37, закомментированный «пример материализации витрины») и превратить его в короткую заметку «VIEW сейчас, материализуем если затормозит, см. docs/ARCHITECTURE.md». Добавить «поток данных» в шапку 40_dds_to_dm.sql.
  • Минор: sql/ddl/dm/40_dm.sql — магическое 1919 в groupArraySample (строка «зачем» или упростить).
  • Сами витрины DM показываем в деле в уроках 5–6, отдельного разбора не делаем.

Урок 4 — Airflow DAG (airflow/dags/etl_pipeline_dag.py):

  • Главная правка (код↔доки): сделать check_dds_integrity честным гейтом — добавить assert_dds_integrity (PythonOperator), роняющий DAG при orphan_events > 0. Сейчас «проверка» только считает сирот в xcom и пишет их в dq_summary, но DAG остаётся зелёным, хотя LESSON_STANDARD §3 и PRD §2 (цель 3) обещают остановку при нарушении целостности. После правки доки и код сходятся.
  • Управляемая правка урока 4 строится на этом гейте: менти намеренно ломает целостность (вставляет «осиротевшее» событие) и видит, как DAG краснеет на assert_dds_integrity. Опирается на понятие сирот из урока 3.
  • Добавить ASCII-поток задач в docstring DAG (precheck → transform: wait → ods → dq → branch → dds → integrity → dm → validate) — для теста одного прохода.
  • Замечание: check_ods_quality тоже measure-only (метрики в xcom, без гейта) — это ок и намеренно (DQ по parse_errors информативен, но не блокирует). В тексте урока развести: какие проверки гейтят пайплайн, а какие только измеряют.

Урок 5 — мониторинг (configs/*):

  • Конфиги (prometheus.yml, экспортёры, provisioning Grafana) полировать под «учебное качество кода» не нужно: дашборды/JSON не читаются построчно. Вся учебная нагрузка ложится на текст урока (маршрут по дашбордам + смысл панелей).
  • При написании проверить, что имена дашбордов/панелей/метрик в тексте совпадают с реальными (configs/grafana/provisioning/dashboards/*.json).
  • Кандидат на мини-правку (зеркало урока 4): погасить сервис → увидеть, как панель/ алерт в Grafana краснеет. Решает открытый вопрос PRD про глубину урока (даёт «сломал-увидел» вместо чистого наблюдения) — обсудить при написании.

4. Что дальше

Все уроки 0–6 и лабы 07–08 написаны (lessons/). Правки кода §3.1 применены в составе уроков; чекбоксы выше сверены с реальным кодом 2026-06-06. Маршрут курса собран полностью.

Исходный план фазы написания (оставлен как контекст): пишем уроки по одному, по шаблону из LESSON_STANDARD.md, начиная с урока 1 (Kafka→CH); урок 0 (разминка на Kafka UI) готовим параллельно — он не зависит от полировки кода; правки кода из §3.1 применяем в составе соответствующего урока.