- Зачем: начинающим трудно читать задания из-за необъясненных терминов и неестественных формулировок. - Что: переработаны вводная и 21 задание, названы темы разделов, объяснены термины и сокращены повторы; согласованы правила учебного текста и ссылки. - Проверка: сохранены 25 блоков SQL и команд и таблицы состава колонок; проверены 153 локальные ссылки и 32 входящие ссылки на разделы; git diff --check прошел.
392 lines
34 KiB
Markdown
392 lines
34 KiB
Markdown
# Программа учебных лаб
|
||
|
||
Эта спека собирает программу лаб из
|
||
[инвентаризации сюжетов](../research/2026-08-31-labs-inventory.md), выполненной
|
||
в тикете #201. Тикет спеки — #202, карта — #182.
|
||
|
||
Статус: спека принята владельцем; судьба идей-кандидатов решена здесь поименно.
|
||
|
||
## 1. Кому и зачем
|
||
|
||
Менти уже прошел большой теоретический курс ClickHouse объемом около 60 часов.
|
||
Он знает устройство системы в теории, но почти ничего не делал своими руками.
|
||
|
||
Поэтому в программе нет вводного блока с теорией. Здесь теория становится
|
||
ремеслом: менти руками осваивает слои хранилища, DQ, оркестрацию и
|
||
идемпотентность.
|
||
|
||
Обещание программы: менти проходит конвейер стенда автором, а не читателем.
|
||
|
||
## 2. Устройство программы: основная программа и факультативы
|
||
|
||
У частей программы два статуса, третьего нет.
|
||
|
||
**Основная программа** — обязательная часть. Менти проходит ее целиком и по
|
||
порядку, потому что каждая следующая ступень опирается на предыдущую. От
|
||
ступени к ступени растет самостоятельность: сначала менти получает готовые ТЗ с
|
||
готовыми сторожами (DQ-проверками), а затем строит сторожей сам.
|
||
|
||
**Факультативы** — необязательные лабы. Они собраны в тематические разделы,
|
||
чтобы менти мог одним взглядом выбрать интересное. Для каждого раздела указано,
|
||
после какой ступени основной программы он открывается.
|
||
|
||
В программе используются четыре формы заданий:
|
||
|
||
- *построить свое по ТЗ* — рядом есть образец, но готового ответа нет;
|
||
- *вычитание* — SQL готовой трансформации удаляется и пишется заново, а даги
|
||
остаются как несущая инфраструктура;
|
||
- *собери рядом* — менти строит свою копию результата рядом с нетронутым
|
||
стендовым и сверяет их построчно;
|
||
- *расследование* — данные уже лежат на стенде или стенд намеренно ломается, а
|
||
менти ищет причину и объясняет ее.
|
||
|
||
### Как писать задания
|
||
|
||
Пишите для учащегося: объясняйте, чему он научится, зачем нужны действия
|
||
и как понять результат. Историю разработки оставляйте за пределами задания.
|
||
|
||
Начинайте задание коротким описанием предстоящей работы. Заголовки должны
|
||
называть содержание разделов: «Подготовка», «Создание витрины», «Проверка
|
||
результата». Шаблонные вопросы «Чему учит» и «Что сделать» не нужны.
|
||
|
||
Раздел проверки описывает действия для проверки и поиска ошибок.
|
||
Раздел результата перечисляет итог работы и то, что учащийся теперь может
|
||
объяснить. Если проверка уже встроена в шаги упражнения, повторять ее
|
||
отдельным разделом не нужно.
|
||
|
||
Структуру выбирайте по содержанию. Образцы — задания
|
||
про [дашборд Superset](../labs/optional/superset-dashboard.md) и
|
||
[расследование подключения](../labs/optional/superset-missing-object.md).
|
||
|
||
## 3. Основная программа — семь ступеней
|
||
|
||
### Ступень 1. Построй пять витрин слоя DM
|
||
|
||
Первая часть программы опирается на шесть существующих заданий из
|
||
[`docs/labs/`](../labs/README.md). Пять ТЗ по витринам входят в первую ступень
|
||
основной программы и выполняются как есть. Их точечный пересмотр — работа
|
||
реализации. Шестое задание, про свой дашборд в Superset, остается факультативом
|
||
в разделе «Superset и BI». Набор уроков пяти витрин уже записан в их общем
|
||
описании: от слоя как договора до честных имен показателей.
|
||
|
||
Форма — построить свое по ТЗ. Обратную связь дает даг `homework_check`.
|
||
|
||
### Ступень 2. Свой отчет в Superset
|
||
|
||
Менти строит один график в Superset поверх витрины, которую сделал на первой
|
||
ступени. Урок — увидеть, как собственный результат из хранилища становится
|
||
отчетом в BI, не превращая знакомство с инструментом в отдельный большой
|
||
проект.
|
||
|
||
Форма — построить свое по ТЗ. Результат проверяется глазами. ТЗ для этой новой
|
||
ступени предстоит написать при реализации программы. Полный собственный дашборд
|
||
остается факультативом в разделе «Superset и BI».
|
||
|
||
### Ступень 3. Нарежьте сессии сами
|
||
|
||
До того как открыть стендовый SQL нарезки сессий в DDS, менти самостоятельно
|
||
делит события на визиты. Сначала он сравнивает число своих сессий с эталоном
|
||
`VisitID`. Затем вручную соединяет свою сессию с эталонной и поштучно разбирает
|
||
хотя бы одно расхождение границ. Урок — счет называет проблему, но причину
|
||
приходится искать в отдельных строках.
|
||
|
||
Форма — построить свое по ТЗ и провести расследование. Обратная связь состоит
|
||
из счетной сверки с `VisitID` и ручного сопоставления границ.
|
||
|
||
### Ступень 4. Перепишите нарезку стенда
|
||
|
||
Менти удаляет и заново пишет SQL нарезки сессий DDS: расчет объема, пересборку
|
||
и замену. Урок — изменить расчетную сущность внутри готового конвейера и
|
||
проверить ее внешним эталоном. Это единственный поток, для которого
|
||
инвентаризация подтвердила вычитание без оговорок: здесь есть настоящие места
|
||
для ошибок, а проверка не содержит готового ответа.
|
||
|
||
Форма — вычитание. Обратную связь дают `dq_check` и поштучная сверка,
|
||
построенная на третьей ступени.
|
||
|
||
### Ступень 5. Научите проверки замечать ошибки в заказах
|
||
|
||
Сначала менти строит две недостающие DQ-проверки. Первая утверждает равенство
|
||
«цена × количество = items_total». Вторая сверяет слой с источником по дням. Их
|
||
менти строит по образцу пяти стендовых проверок в `sql/dq/`. Обе проверки
|
||
должны покраснеть на управляемой ошибке. Затем менти вручную подкладывает по
|
||
строке каждого класса брака, проверяет разбор и убирает эти строки за собой.
|
||
|
||
Только после этого он вычитает разбор заказов ODS `order_parse` и трансформацию
|
||
заказа DDS. Урок — сторожа строят до рискованного изменения, а не после первой
|
||
поломки.
|
||
|
||
Форма — построить свои проверки по ТЗ, провести управляемое расследование и
|
||
выполнить вычитание. Обратную связь дают две новые проверки, баланс годных и
|
||
ошибочных строк по срезу и существующие сверки заказов.
|
||
|
||
### Ступень 6. Разбери сырые события и сверь результат со стендом
|
||
|
||
Менти пакетно разбирает срез событий из STG в собственную таблицу и построчно
|
||
сверяет результат с нетронутым стендовым ODS. Урок — изучить разбор потока
|
||
отдельно от живого приема и использовать стендовый результат как образец для
|
||
сверки. Матвью разбора остаются несущей стеной: их пересоздание молча теряет
|
||
сообщения.
|
||
|
||
Форма — «собери рядом». Обратная связь — построчная сверка двух разборов. Ее
|
||
границы называются прямо: срок работы ограничен окном хранения сырых данных, а
|
||
содержимое неопорных колонок можно сравнить только со стендовым разбором, не с
|
||
независимым эталоном.
|
||
|
||
### Ступень 7. Мониторинг: куда смотреть
|
||
|
||
Сначала менти знакомится с панелями стенда. Затем проводит два безопасных
|
||
расследования. В первом он находит в `query_log`, что верх списка тяжелых
|
||
запросов занимает сам мониторинг. Во втором объясняет, почему свежесть второго
|
||
источника выглядит как пила, а не как линия, и выбирает правильный слой данных
|
||
для панели.
|
||
|
||
Форма — расследование. Обратная связь — найденные запросы и верное объяснение
|
||
формы графика и выбора слоя. Ломательные опыты остаются в факультативе «Что
|
||
делать, когда стенд сломался». Урок ступени — куда и на что смотреть в живой
|
||
системе.
|
||
|
||
**Общее правило вычитания:** ТЗ каждого вычитания на ступенях 4 и 5 включает
|
||
пересчет ключа шардирования по `hostName()`. Счетные сверки читают `_dist`,
|
||
поэтому учитывают строку даже на чужом шарде и остаются зелеными: они не видят
|
||
саму ошибку раскладки.
|
||
|
||
## 4. Факультативы — пять разделов
|
||
|
||
Ниже перечислены сюжеты, их уроки и принятые решения по форме заданий. Исходный
|
||
материал и способы проверки — в
|
||
[инвентаризации](../research/2026-08-31-labs-inventory.md).
|
||
|
||
### Почему выручка не сходится
|
||
|
||
Этот раздел рекомендован первым из факультативов и открывается после ступени
|
||
1 основной программы. Шесть сюжетов собраны в четыре лабораторные: три
|
||
расследования готовых данных и одно наблюдение во времени. Опоздания,
|
||
изменение выручки и история отмен используют одну историю заказов, поэтому
|
||
для них выбрана общая работа вместо трех отдельных.
|
||
|
||
Результат расследования — запрос сверки и инженерное решение, подтвержденное
|
||
данными: выбор источника, ключа, полей для точного расчета или правила
|
||
пересчета и хранения истории. Объяснение суммы служит проверкой решения.
|
||
Вариант закончить работы только объяснением показателей отклонен: он
|
||
смещал акцент в анализ отчетов. Постройка новых DAG и подключение проверок
|
||
к конвейеру оставлены основной программе, чтобы факультативы не повторяли
|
||
ее задания.
|
||
|
||
- **[Куда пропали покупки](../labs/optional/missing-purchases.md) (#186).**
|
||
Сверка по номерам заказов обосновывает источник витрины и условие
|
||
окончательной проверки полноты. Счетчики событий помогают обнаружить
|
||
расхождение, но не устанавливают место потери.
|
||
- **[Почему повтор покупки завышает выручку](../labs/optional/duplicate-purchases.md) (#187).**
|
||
Учащийся собирает расчет с одной строкой на заказ, проверяет ключ и полноту
|
||
набора номеров, отличает его зерно от зерна исходных событий.
|
||
- **[Какая позиция объясняет разницу в сумме](../labs/optional/order-amount-difference.md).**
|
||
Сверка позиций обосновывает выбор исходных полей и типа денег, отделяет
|
||
ошибку собственного разбора от различия данных события и заказа.
|
||
- **[Почему вчерашняя выручка меняется сегодня](../labs/optional/revenue-over-time.md) (#188).**
|
||
Наблюдение опозданий и смены статусов обосновывает правило пересчета при
|
||
ежедневной загрузке без пропусков. Учащийся определяет момент закрытия дня
|
||
и требования к истории для воспроизведения расчета и проверки факта оплаты.
|
||
|
||
В четвертой лабораторной выбрано собственное наблюдение: учащийся продвигает
|
||
мир по одному дню через Airflow и сохраняет результаты после обновления витрин
|
||
в две локальные таблицы ClickHouse под `student`: состояния заказов и итоги
|
||
наблюдений. Сравнение выполняется SQL-запросами в том же подключении DBeaver.
|
||
Учащийся может писать только в `default`; источники доступны ему на чтение.
|
||
Отдельная строка итогов фиксирует и наблюдение без заказов. Экспорт в CSV
|
||
отклонен как основной способ: он требовал неописанного перехода к другому
|
||
инструменту или обратному импорту файлов.
|
||
Готовые слепки и сочетание готового примера с собственным наблюдением
|
||
отклонены: нужную историю можно получить на стенде и самому увидеть,
|
||
как новые данные меняют уже рассчитанный отчет.
|
||
|
||
День считается закрытым после получения и обработки последнего слепка
|
||
семидневного окна изменяемости; этот слепок доставляется следующим модельным
|
||
днем. Одного совпадения сумм в соседних наблюдениях для такого вывода
|
||
недостаточно. История отмен тоже имеет границу: оплата и отмена между двумя
|
||
дневными слепками могут остаться невидимыми. Отсутствие наблюдаемой оплаты
|
||
не доказывает, что оплаты не было.
|
||
|
||
### Тонкий анализ данных
|
||
|
||
Открывается после ступени 1 основной программы.
|
||
|
||
- **[Как новые заказы меняют отчет о посетителях](../labs/optional/identity-over-time.md) (#145).**
|
||
Учащийся собирает связи с подтверждающими покупками и сохраняет три
|
||
ежедневных наблюдения. На одном наборе прежних сессий сравнивает расчет
|
||
с прежними и новыми связями, выбирает историю для воспроизведения расчета.
|
||
- **Добавь названия регионов в отчет (#143).** Менти собирает свой словарь по
|
||
образцу каталога и обогащает отчет через `dictGet`.
|
||
|
||
Работа о связях продолжается в инженерном разделе лабораторной про
|
||
инкрементальный `daily_traffic`. В обеих учащийся продвигает мир через
|
||
`world_next_day` и `etl_pipeline`, сохраняет наблюдения под `student` в
|
||
свои локальные таблицы `default` и сравнивает их SQL-запросами.
|
||
|
||
Время первой подтверждающей покупки и время обнаружения связи разделены.
|
||
Минимум времени `purchase` не принят за границу появления новых связей:
|
||
заказ может поступить позже события. Нужные изменения определяются по
|
||
разности сохраненных наборов пар. Ежедневное наблюдение ограничивает момент
|
||
появления связи интервалом; для пар начального наблюдения более ранняя
|
||
история неизвестна. История сессий в этом опыте растет без исправлений
|
||
прошлых дней, а связи не отзываются — поэтому сохраненных наборов связей
|
||
достаточно для сравнения расчетов на прежних сессиях.
|
||
|
||
### Superset и BI
|
||
|
||
Открывается после ступени 2 основной программы.
|
||
|
||
- **[Свой дашборд](../labs/optional/superset-dashboard.md).** Менти использует
|
||
результат ступени 2, собирает отчет из пяти своих витрин и подтверждает
|
||
выводы запросами. Пользовательские объекты остаются отдельно от стендовых.
|
||
- **[В DBeaver работает, в Superset нет](../labs/optional/superset-missing-object.md).**
|
||
Менти создает отдельное представление на первой ноде и расследует ошибку
|
||
чтения из Superset, подключенного ко второй. Опыт заканчивается объяснением,
|
||
исправлением DDL и удалением учебного объекта.
|
||
|
||
Задания самостоятельны: расследование использует готовую витрину стенда.
|
||
Отсутствие объекта проверяется в SQL Lab, поскольку создание физического
|
||
датасета может остановиться раньше, чем появится график. Чтение только одного
|
||
шарда остается материалом инженерного раздела: это отдельная причина
|
||
неполного результата, которую добавление `ON CLUSTER` само по себе не устраняет.
|
||
|
||
Технический опыт проверен 2026-09-13 на ClickHouse 26.3.17.56: локальное
|
||
представление недоступно пользователю `bi` на второй ноде; после создания
|
||
на кластере обе ноды возвращают тот же счет, что исходная витрина.
|
||
|
||
### Инженерия стенда
|
||
|
||
Открывается после ступени 1 основной программы. Сюжеты про переигрывание дня и
|
||
пошардовую атомарность раскроются полнее после ступени 4, когда менти уже
|
||
спускался в DDS.
|
||
|
||
- **Как принимать данные из Kafka: потоком или слепком.** Поток и слепок
|
||
требуют разных способов приема и дают разную наблюдаемость.
|
||
- **Почему данные попадают на свои шарды независимо от ноды чтения.**
|
||
Нода-потребитель может меняться, а раскладку строк определяет ключ
|
||
шардирования.
|
||
- **Почему вторая группа потребителей дублирует поток.** Еще одна группа
|
||
потребителей создает еще одну полную копию потока.
|
||
- **Как заметить пропущенные сообщения по офсетам.** Разрыв в офсетах
|
||
показывает потерю, которую трудно заметить по самим данным.
|
||
- **Почему сырые данные удаляются по времени загрузки.** Очистка сырых данных
|
||
идёт по
|
||
времени загрузки, а не по модельному дню.
|
||
- **Куда попадают битые строки.** Годные строки и брак делят поток без
|
||
пропусков и пересечений; менти подкладывает по строке каждого класса брака
|
||
событий и находит их в `ods.event_errors_dist`.
|
||
- **Какая версия заказа считается текущей.** Версию заказа задает слепок
|
||
источника, а `FINAL` скрывается за представлением.
|
||
- **Почему один момент попадает в разные дни.** Один момент дает разные дни в
|
||
разных поясах, поэтому день и пояс нужно называть явно.
|
||
- **Проверь, как стенд разобрал JSON в `ecommerce`.** Типизированный разбор
|
||
может молча подменить ошибку значением, поэтому менти сам разбирает сырой
|
||
JSON и сравнивает результат с массивами стенда.
|
||
- **Как конвейер восстанавливает пропущенный день.** Конвейер сам находит дыру
|
||
и определяет объем повторной обработки по состоянию хранилища.
|
||
- **Что будет, если день заменился не на всех шардах.** Замена дня завершается
|
||
отдельно на каждом шарде, поэтому целостность результата подтверждает
|
||
отдельная сверка.
|
||
- **[Как обновлять витрину без полной пересборки](../labs/optional/incremental-daily-traffic.md)
|
||
(#149) — повышенная сложность.** Продолжение работы о связях пользователей:
|
||
учащийся строит свою витрину, выбирает затронутые дни по новым сессиям
|
||
и связям, сверяет весь результат с `dm.daily_traffic_v`. Журнал успешных
|
||
загрузок позволяет повторить опыт с частично записанным результатом.
|
||
|
||
Инкрементальная работа начинается с полной загрузки текущего состояния,
|
||
затем охватывает два новых модельных дня. Обновление собственной витрины
|
||
выполнено вставкой версий в `ReplacingMergeTree`, чтение — с `FINAL`.
|
||
Это оставляет права `student` прежними и не требует менять конвейер стенда.
|
||
Версия — дата наблюдения; ключ — день отчета. История связей хранится
|
||
отдельно от версий витрины. Выбор опирается на
|
||
[договор движка](https://clickhouse.com/docs/engines/table-engines/mergetree-family/replacingmergetree):
|
||
максимальная версия по ключу и удаление прежних версий при слияниях.
|
||
DDL, обновление вставкой и чтение с `FINAL` проверены под `student`
|
||
на ClickHouse 26.3.17.56.
|
||
|
||
Полный пересчет остается эталоном, а экономия оценивается числом выбранных
|
||
дней. На малом объеме ускорение не обещано. Поиск связей продолжает читать
|
||
историю источников целиком; инкрементальным становится расчет дневной витрины.
|
||
Полная замена стендового ETL отклонена: для учебного сравнения достаточно
|
||
своей витрины и журнала рядом с работающим конвейером.
|
||
|
||
### Что делать, когда стенд сломался
|
||
|
||
Открывается после ступени 7 основной программы; весь раздел — повышенная
|
||
сложность.
|
||
|
||
- **Что ломается вместе с keeper.** Остановка keeper связывает состояние DDL и
|
||
репликации с тем, что видно на панелях и в системных таблицах.
|
||
- **Где искать запрос DDL, который не завершился.** Очередь распределенного DDL
|
||
показывает путь запроса и ответ каждой ноды.
|
||
|
||
Итог раздела — runbook, которого сейчас не хватает стенду. Менти пишет его по
|
||
результатам опытов, и документ остается в проекте.
|
||
|
||
## 5. Границы вычитания по потокам
|
||
|
||
Подробное сравнение потоков и таблица доказательств остаются в
|
||
[инвентаризации](../research/2026-08-31-labs-inventory.md). Спека берет это
|
||
сравнение за основу и проводит свои границы. Без оговорок вычитанию отдаются
|
||
только сессии DDS. Заказы можно вычитать лишь после самодельных сторожей. Для
|
||
событий ODS выбрана форма «собери рядом»; вычитание здесь отклонено. Прием STG
|
||
и забор заказов остаются несущей стеной и доступны только для расследований.
|
||
Карту идентичностей спека решает не отдавать вычитанию; причины этого выбора
|
||
названы в следующем разделе.
|
||
|
||
## 6. Что не вошло
|
||
|
||
Вычитание стендовых витрин DM не входит в программу. Оно повторяет урок
|
||
готового ядра DM, а открытые сверочные запросы в `sql/dq/` почти дословно
|
||
содержат ответ.
|
||
|
||
Форма «собери рядом» для карты идентичностей тоже не входит. Независимого
|
||
эталона нет, а слабые инварианты проверяют допустимость связей, но не полноту.
|
||
Аналитический урок идентичности уже несет склейка во времени (#145) в
|
||
факультативе «Тонкий анализ данных».
|
||
|
||
Сюжеты, которые инвентаризация относит к лекциям, остаются лекциями: `Sign` и
|
||
CollapsingMergeTree, топологии в бою и идентичность «как в бою». Эта спека
|
||
оставляет их в
|
||
[списке опорных точек раздела 12 спеки v2](2026-07-30-stand-v2-realism.md#опорные-точки-для-будущих-лекций-и-лаб).
|
||
|
||
## 7. Судьба идей-кандидатов
|
||
|
||
Эти решения закрывают тикеты после приемки спеки:
|
||
|
||
- #143, справочник регионов — вошла в факультатив «Тонкий анализ данных».
|
||
- #145, склейка во времени — вошла в факультатив «Тонкий анализ данных».
|
||
- #148, домашки ниже DM и вычитание — вошла в основную программу как ступени
|
||
4–6. Форму для каждого потока уточнила инвентаризация #201: вычитание,
|
||
«сначала сторож» или «собери рядом».
|
||
- #149, инкрементальный `daily_traffic` — вошла в факультатив «Инженерия
|
||
стенда» с пометкой «повышенная сложность».
|
||
- #186, воронка подтверждений — вошла в факультатив «Почему выручка не
|
||
сходится».
|
||
- #187, бизнес-дубль — вошла в факультатив «Почему выручка не сходится».
|
||
- #188, опоздавшие заказы — вошла в факультатив «Почему выручка не сходится».
|
||
|
||
## 8. Отклоненные варианты оси
|
||
|
||
- **Чистое «снизу вверх»: STG → ODS → DDS → DM.** Оно начинает программу с
|
||
самого тяжелого по инфраструктуре участка. Внизу быстро кончается эталон
|
||
обратной связи, а вычитание вслепую заставляет переписывать слой, которым
|
||
менти еще не пользовался.
|
||
- **Два прохода: сначала расследования снизу вверх, потом постройка сверху
|
||
вниз.** Пролог из восьми расследований откладывает первый собственный
|
||
результат. Вместо этого расследования разнесены по факультативам и
|
||
открываются по мере прохождения основной программы.
|
||
- **Сюжетами поперек слоев.** Такой порядок ломает принятое решение, по
|
||
которому готовые задания открывают программу, и превращает путь в сетку
|
||
зависимостей.
|
||
|
||
## 9. Границы спеки и что дальше
|
||
|
||
Спека не переписывает существующие ТЗ в `docs/labs/` и общий README; точечный
|
||
пересмотр относится к реализации.
|
||
|
||
Реализация программы тоже находится за границей спеки. В нее входят новые ТЗ
|
||
для ступеней 2–7 основной программы и факультативов, а также отражение порядка
|
||
программы в `docs/labs/README.md`. Карту #182 приемка этой спеки закрыла;
|
||
исполнение ведет отдельная карта — тикет #206 (решение тикета #203).
|