- Зачем:
- перечитка урока 3 свежим взглядом нашла баг в его эталонном пути: проверки
device_not_found/geo_not_found/location_not_found в DDS никогда не срабатывали,
а текст урока ошибочно утверждал, что метки ставятся
- Что:
- sql/dds/30_ods_to_dds.sql: добавлен SETTINGS join_use_nulls=1 в оба
INSERT...SELECT. Без него LEFT JOIN на несовпадении клал в assumeNotNull(click_id)
нулевой UUID (не NULL), и if(...IS NULL, ['*_not_found'], []) молча давал []
(мёртвый код). Тот же класс бага про типы/NULL, что kafka_ts в уроке 1
- docs/course/lessons/03_ods_to_dds.md: убраны ложные claim'ы про geo_not_found/
location_not_found, формулировки приведены к реальному поведению (клик без гео
остаётся с пустыми полями NULL; целостность событий — через orphan_events);
поправлена опечатка «список всех клиентов» → «всех кликов»
- Проверка:
- синтетический тест join_use_nulls=1: клик в device без geo → в ods_parse_errors
появляются geo_not_found и geo_country_missing (до фикса — пусто)
- LIMIT=50 make transform после фикса: dds.click=26, dds.event=50,
orphan_events=0, ни одной строки с непустым ods_parse_errors (вывод не изменился —
на чистом срезе несовпадений нет)
- Зачем:
- урок 4 должен показывать не только измерение сирот в DDS, но и остановку Airflow DAG при нарушении связи dds.event -> dds.click.
- Что:
- добавлен assert_dds_integrity в etl_pipeline и документация управляемого красного сценария.
- вынесены общие helper'ы для SQL-split и boolean-параметров Airflow.
- добавлен урок 4 и обновлены навигация курса, план обучения и operations notes.
- Проверка:
- python3 -m py_compile airflow/dags/etl_pipeline_dag.py airflow/dags/ddl_init_dag.py airflow/dags/kafka_load_dag.py airflow/dags/utils/airflow_params.py airflow/dags/utils/sql_helpers.py.
- docker compose exec -T airflow-webserver airflow dags test etl_pipeline 2026-06-05T18:00:00 -c '{"full_refresh": true}'.
- Зачем:
- собрать разрозненные кусочки ODS в цельные сущности DDS и ввести понятие
целостности связей (сироты), пока без жёсткого гейта — он в уроке 4
- Что:
- добавлен docs/course/lessons/03_ods_to_dds.md: сущности dds.click/dds.event,
UNION-универсум кликов, дедуп через argMax, LEFT JOIN, понятие сироты,
управляемая правка (вставка сироты), recap STG→ODS→DDS, заметка про DM
- §3.1: «поток данных» в шапку sql/dds/30_ods_to_dds.sql
- демоут DM: убран закомментированный пример материализации в
sql/dm/40_dds_to_dm.sql, добавлены «поток данных» и заметка «VIEW сейчас,
материализуем если затормозит» со ссылкой на docs/ARCHITECTURE.md
- sql/ddl/dm/40_dm.sql: пояснён seed 1919 в groupArraySample, поправлен
неверный комментарий «последние» (groupArraySample берёт случайную выборку)
- README курса: индекс обновлён до «уроки 0–3»
- Проверка:
- LIMIT=50 make transform: dds.click=26, dds.event=50, orphan_events=0
- §4 на стенде: вставка события-сироты → orphan 0→1; LEFT JOIN в
dm.v_events_enriched даёт NULL по полям клика; make transform откатывает к 0
- /ai-text-lint (article): house style сохранён, AI-маркеры не найдены
- Зачем:
- нужен учебный урок «руки» про типизацию слоя ODS и разделение
чистых/битых записей; по пути убрать мусор и неочевидности в
эталонном пути, чтобы он читался за один проход.
- Что:
- добавлен lessons/02_stg_to_ods.md по LESSON_STANDARD (6 секций,
режим «руки», эталон голоса — урок 1); регистр смягчён под уровень
«обзорно» с расшифровкой терминов (click-контекст, WITH, двойной учёт).
- 20_stg_to_ods.sql: поток данных в шапку + блок «DQ-split» (почему
строка может попасть и в основную таблицу, и в *_errors).
- 20_ods.sql: убран мусорный блок из 8 DROP TABLE mv_*_to_ods;
пояснено разное партиционирование (browser — по бизнес-дате,
click-контекст — по дате загрузки).
- Проверка:
- make ddl && make transform — проходят чисто, counts не изменились
(browser/location 50, device/geo 26 дедуп, *_errors 0).
- правка §4 (toFloat64OrNull→toInt64OrNull для geo_latitude) на стенде
даёт geo_by_click_errors 0→50 и NULL-широту с флагом bad_geo_latitude.
- Зачем:
- нужен первый урок курса по эталонному пути STG, а рамка курса описывала сопровождение как «сессию-сверку», хотя по факту это самостоятельная работа + еженедельный созвон.
- Что:
- добавлен docs/course/lessons/01_kafka_to_clickhouse.md (Kafka → ClickHouse, слой STG) по шаблону LESSON_STANDARD.
- в sql/ddl/stg/10_stg.sql исправлен баг kafka_ts во всех 4 MV: toInt64(DateTime64) срезал миллисекунды, kafka_ts по всему стенду был 1970-01-21; теперь _timestamp_ms присваивается напрямую (downstream на kafka_ts не опирается).
- урок 1 §3/§4 приведены к исправленному коду; врезка про рассинхрон MV↔таблица описывает реальное поведение (молчаливый сброс лишней колонки, не ошибка).
- «сессия/сессия-сверка» → «созвон» в PRD (датированная поправка), LESSON_STANDARD §6 (секция «Что должно получиться») и README курса; добавлена ссылка на открытый DE-роадмап.
- в LEARNING_PLAN исправлен вердикт аудита урока 1 (баг найден прогоном), war-story про toInt64(DateTime64) припаркована в урок 2.
- Проверка:
- прогон на стенде: make up && make ddl && LIMIT=50 make data → kafka_ts = 2026-… с миллисекундами; правка §4 (ALTER + пересоздание MV + TRUNCATE + перезаливка) и откат отработали.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move DDL files from flat ddl/ directory to sql/ddl/ with layer-based
subdirectories (stg, ods, dds, dm). Move batch transformation SQL from
jobs/ to sql/ layer directories. Update scripts and documentation to
reflect new paths for improved organization and Airflow integration.