diff --git a/docs/course/LEARNING_PLAN.md b/docs/course/LEARNING_PLAN.md index 7349f04..ce2dcdb 100644 --- a/docs/course/LEARNING_PLAN.md +++ b/docs/course/LEARNING_PLAN.md @@ -1,19 +1,26 @@ # План обучения: курс «Кликстрим на ClickHouse» -> Статус: черновик (скелет). Дата: 2026-06-01. +> Статус: черновик. Дата: 2026-06-03 (аудит путей выполнен; середина расщеплена — +> один паттерн на урок, всего 7 уроков, см. §1–2). > Назначение: высокоуровневый маршрут менти по курсу — карта уроков, порядок, > результаты аудита эталонных путей. Рамка курса (зачем/что/скоуп) — в `PRD.md`; > как устроен отдельный урок — в `LESSON_STANDARD.md`. > -> Этот файл — стартовый скелет. Детальный аудит путей и финальные вердикты -> дописываются перед стартом уроков. +> Раздел 3 содержит финальные вердикты аудита и конкретный список правок — +> их применяем при написании соответствующих уроков, а не отдельным забегом. --- ## 1. Маршрут -Порядок линейный — уроки 0→4 идут цепочкой, повторяя сам пайплайн. Мониторинг (4) -и Superset (5) более самостоятельны и работают как надстройки. +Порядок линейный — уроки 1→4 повторяют сам пайплайн (STG → ODS → DDS → оркестрация), +а урок 0 — разминка перед ним. Мониторинг (5) и Superset (6) более самостоятельны и +работают как надстройки поверх готовых данных. + +Принцип нарезки — **один прод-паттерн на урок** (`LESSON_STANDARD` §2). Поэтому +середина пайплайна разнесена: типизация+DQ (ODS) и сборка сущностей (DDS) — разные +уроки. Витрины DM **не отдельный урок**: их показываем в деле там, где их потребляют — +в мониторинге (DQ-витрины) и BI (traffic/utm-витрины). **Что нужно знать заранее по Kafka:** перед уроком 0 менти смотрит обзорное видео по Kafka из роадмапа — оно даёт словарь терминов. Урок 0 связывает этот словарь с тем, @@ -25,33 +32,107 @@ Kafka из роадмапа — оно даёт словарь терминов. |---|------|-------------------------------|--------|-------| | 0 | Вводный урок по Kafka | Kafka UI: топики, партиции, offset'ы, consumer-группы, отставание (lag) | обязательный | наблюдение | | 1 | Заземление Kafka → ClickHouse | `sql/ddl/stg/10_stg.sql` (Kafka engine → MV → MergeTree) | обязательный | руки | -| 2 | Слоёный ETL: где MV, а где батч | `sql/ods/*`, `sql/dds/*`, `sql/dm/*` (батч поверх стримингового STG) | обязательный | руки | -| 3 | Оркестрация в Airflow | `airflow/dags/etl_pipeline_dag.py` (зависимости, проверки, остановка при нарушениях) | обязательный | руки | -| 4 | Мониторинг | Prometheus + Grafana + экспортёры | обязательный | наблюдение | -| 5 | BI-витрина | Superset поверх ClickHouse | опциональный | руки | +| 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 | опциональный | руки | -Урок 0 — обязательная разминка (без правок кода, только наблюдение); уроки 1–3 — -с управляемыми правками; урок 4 ближе к наблюдению (глубину уточняем, см. +Урок 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): -| Путь | Файлы | Вердикт (предв.) | Заметки | -|------|-------|------------------|---------| -| STG (Kafka→CH) | `sql/ddl/stg/10_stg.sql` | годно как есть | внятные русские комментарии, метаданные доставки, обработка ошибок, партиционирование | -| ETL слои | `sql/ods/*`, `sql/dds/*`, `sql/dm/*` | точечно править | логика и комментарии крепкие; проверить «тест одного прохода» | -| Airflow DAG | `airflow/dags/etl_pipeline_dag.py` | под вопросом | нужен детальный аудит | -| Мониторинг | `configs/*`, экспортёры | под вопросом | нужен детальный аудит | +| Урок | Путь | Файлы | Вердикт | +|------|------|-------|---------| +| 1 | STG (Kafka→CH) | `sql/ddl/stg/10_stg.sql` | **годно как есть** (+1 строка «зачем») | +| 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/*`, экспортёры | **годно как есть** (для режима наблюдения) | -Финальные вердикты и список конкретных правок — дописать перед стартом уроков. +> Карты путей раздела 2 включают и DDL целевых таблиц (`sql/ddl/{ods,dds}/*`), а не +> только батч-трансформации: без формы целевых таблиц слой читается неполно. + +### 3.1. Список правок (применяем при написании урока) + +Правки точечные, делаем не отдельным забегом, а в составе соответствующего урока. + +**Урок 1 — STG (`sql/ddl/stg/10_stg.sql`):** +- [ ] Добавить одну строку «зачем» к `fromUnixTimestamp64Milli(toInt64(_timestamp_ms))` + в MV — это единственный неочевидный трюк файла (конвертация Kafka-таймстампа). +- Колоночные комментарии-пересказы (`-- Партиция`, `-- Имя топика`) безвредны — не трогаем. +- В финале урока — мост к уроку 2: вопрос «почему дальше не MV?». + +**Урок 2 — STG→ODS, типизация + DQ-split (`sql/ods/20_stg_to_ods.sql` + DDL `sql/ddl/ods/20_ods.sql`):** +- [ ] Добавить однострочный «поток данных» в шапку `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. Что дальше -После согласования этого плана — пишем уроки по одному, по шаблону из -`LESSON_STANDARD.md`, начиная с урока 1 (Kafka→CH). Урок 0 (разминка на Kafka UI) -можно готовить параллельно — он не зависит от полировки кода. +Пишем уроки по одному, по шаблону из `LESSON_STANDARD.md`, начиная с урока 1 +(Kafka→CH). Урок 0 (разминка на Kafka UI) можно готовить параллельно — он не зависит +от полировки кода. Правки кода из §3.1 применяем в составе соответствующего урока. diff --git a/docs/course/LESSON_STANDARD.md b/docs/course/LESSON_STANDARD.md index e10721f..1df84de 100644 --- a/docs/course/LESSON_STANDARD.md +++ b/docs/course/LESSON_STANDARD.md @@ -19,9 +19,15 @@ Например: добавить поле в Materialized View и увидеть его в `*_raw`; «сломать» запись и увидеть `+1` в таблице ошибок `parse_errors`; сменить `kafka_group_name` и увидеть, как топик читается заново. Менти меняет — видит эффект — объясняет. + **Верни как было** — каждая правка завершается явным шагом отката к чистому + состоянию (откатить изменение либо `make clean/up/ddl/data/transform`), чтобы + самостоятельный менти не застрял со сломанным стендом без ментора. (Урок 0 — без этого шага, только наблюдение.) 5. **Проверь себя** — самопроверка (раздел 3). -6. **Принеси на сессию** — что доложить ментору и какие вопросы задать. +6. **Принеси на сессию** — **конкретный артефакт** плюс вопросы: скрин видимого + результата правки (например, красный DAG или `+1` в таблице ошибок), один абзац + «своими словами» про паттерн урока или запрос, который пришлось написать. Артефакт + делает самопроверку проверяемой, а сверку — предметной (критерий успеха `PRD` §5). ## 2. Стандарт качества эталонного кода diff --git a/docs/course/PRD.md b/docs/course/PRD.md index cf5c573..34ef49c 100644 --- a/docs/course/PRD.md +++ b/docs/course/PRD.md @@ -1,6 +1,11 @@ # PRD: продвинутый курс «Кликстрим на ClickHouse» (со звёздочкой) > Статус: черновик (прообраз PRD). Дата: 2026-06-01. +> Поправка 2026-06-03 (разморозка по делу): середина пайплайна расщеплена — ODS и DDS +> теперь разные уроки (принцип «один паттерн на урок»), витрины DM демотированы в +> поверхность потребления. Обязательных уроков стало 0–5, опциональный Superset — урок 6. +> Затронуты §4 (скоуп) и §3/§5 (ожидаемый такт — ~день на урок). Это изменение рамки, +> а не план реализации. > Назначение документа: зафиксировать для будущих сессий, что это за курс, зачем > он, что входит в скоуп работ, а что нет. Это договорная **рамка**, а не план > реализации и не стандарт уроков (см. раздел «Связанные документы»). @@ -55,7 +60,9 @@ Kafka → ClickHouse → BI). но затачиваем под реальный первый прогон, а не под гипотетических будущих менти. - **Режим:** самостоятельный, асинхронный. Менти клонирует репозиторий, поднимает стенд у себя (`make up`) и идёт по урокам из `docs/course/` рядом с кодом. -- **Роль ментора:** еженедельная сессия-сверка, без построчного разбора кода. + Уроки короткие и односоставные — ожидаемый срок прохождения одного **около дня**. +- **Роль ментора:** еженедельная сессия-сверка (покрывает несколько уроков), без + построчного разбора кода. - **Железо:** стек тяжёлый (Kafka + ClickHouse + Airflow + Superset + Prometheus + Grafana одновременно). Считаем наличие подходящего железа данностью; стек не режем на части — это усложнило бы жизнь и менти, и автору материала. @@ -63,8 +70,12 @@ Kafka → ClickHouse → BI). ## 4. Скоуп ### Входит -- **Уроки 0–4 (обязательные):** вводный урок по Kafka, заземление Kafka→CH, слоёный - ETL (где Materialized View, а где батч), Airflow, мониторинг. +- **Уроки 0–5 (обязательные):** вводный урок по Kafka, заземление Kafka→CH, STG→ODS + (типизация + DQ), ODS→DDS (сборка сущностей), Airflow, мониторинг. Принцип нарезки — + один прод-паттерн на урок; контраст «где Materialized View, а где батч» проходит + мостом уроков 1→2. +- Витрины **DM — не отдельный урок**: их показываем в деле там, где их потребляют + (мониторинг и BI). См. `LEARNING_PLAN.md` §1–2. - **Аудит и точечная полировка эталонных путей** этих уроков до учебного качества (стандарт — в `LESSON_STANDARD.md`). - Учебная часть вокруг каждого эталонного пути по единому шаблону урока. @@ -73,7 +84,7 @@ Kafka → ClickHouse → BI). обучения `LEARNING_PLAN.md`. ### Опционально -- **Урок 5: Superset (BI-витрина).** Делаем, если останется ресурс; обязательные +- **Урок 6: Superset (BI-витрина).** Делаем, если останется ресурс; обязательные уроки он не блокирует. ### Не входит @@ -88,7 +99,8 @@ Kafka → ClickHouse → BI). ## 5. Критерии успеха -- Менти проходит урок за неделю **сам**, без построчного разбора с ментором. +- Менти проходит урок **сам**, без построчного разбора с ментором (ожидаемый срок — + около дня на урок: уроки короткие и односоставные). - Может своими словами объяснить паттерн урока и привязать его к обычной кликстрим-аналитике. - На сессии приносит осмысленные вопросы по сути, а не «застрял на запуске». @@ -98,7 +110,8 @@ Kafka → ClickHouse → BI). Порядок создания артефактов: -1. **`PRD.md`** (этот документ) — рамка: что, зачем, скоуп. **Замороженный** документ. +1. **`PRD.md`** (этот документ) — рамка: что, зачем, скоуп. По умолчанию **заморожен**; + меняется только осознанной поправкой с датой и причиной в шапке (как 2026-06-03). 2. **`LEARNING_PLAN.md`** — высокоуровневый план обучения: карта уроков, маршрут менти, результаты аудита путей. Пишется после согласования PRD. 3. **`LESSON_STANDARD.md`** — стандарт уроков: шаблон урока, стандарт качества кода, @@ -113,8 +126,9 @@ Kafka → ClickHouse → BI). ## 7. Открытые вопросы / на будущее -- Глубина урока 4 (мониторинг): сколько внутреннего устройства показывать против - «просто наблюдай дашборд». +- Глубина урока 5 (мониторинг): сколько внутреннего устройства показывать против + «просто наблюдай дашборд». Кандидат — мини-правка «погаси сервис → алерт краснеет» + (зеркало урока 4), см. `LEARNING_PLAN.md` §3.1. - Нужна ли BI-витрина (Superset) уже в первой версии или переносим на следующую. - Переиспользование: после первого прогона — ретроспектива и обобщение материала под других менти.