Files
clickstream-ch-kafka-supers…/docs/course/LEARNING_PLAN.md
T
ddadminandClaude Opus 4.8 cc1cffe2f3 docs(course): каркас курса и переобвязка уроков 0–6 на путь import (#21)
Зачем: после редизайна пути менти курс ссылался на старый путь
generated-history-analytics/backfill и не проходился по новому стенду.

Что: в README курса — единый блок подготовки и канонического сброса
(make clean -> make up + ddl_init/world_init -> make superset-init),
таблица уроков дополнена лабами 07–08 («в работе»); LESSON_STANDARD и
уроки 0–6 ссылаются на канонический блок; урок 1 переведён на дозаливку
через world_next_day (кнопкой-анонсом, цена в минутах названа); урок 4 —
лесенка DAG-ов; урок 5 — словарь «база import / живой поток»; урок 6
обязателен; цифры старого мира помечены маркером «сверить-на-стенде».

Проверка: grep по generated-history-analytics/backfill в docs/course/
пуст; правки только в docs/course/; git diff --check чистый.

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

15 KiB
Raw Blame History

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

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

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


1. Маршрут

Порядок линейный — уроки 1→4 повторяют сам пайплайн (STG → ODS → DDS → оркестрация), а урок 0 — разминка перед ним. Мониторинг (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–4 — с управляемыми правками; урок 5 ближе к наблюдению (глубину уточняем, см. «Открытые вопросы» в PRD.md).

Где «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 написаны (lessons/), правки кода §3.1 применены в составе уроков — чекбоксы выше сверены с реальным кодом 2026-06-06. Базовый контент курса собран.

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