Факультативы в отдельной папке #245

Merged
ddmitry merged 4 commits from 244-optional-labs into main 2026-09-07 14:54:11 +03:00
6 changed files with 68 additions and 52 deletions

No files matched your search

+22 -10
View File
@@ -8,15 +8,26 @@
Стенд строится по спеке Стенд строится по спеке
[«Боевой реализм стенда (v2)»](docs/specs/2026-07-30-stand-v2-realism.md). [«Боевой реализм стенда (v2)»](docs/specs/2026-07-30-stand-v2-realism.md).
Отсюда вопрос к любой доработке — коду, проверке, документу: **чему на ней Стенд рассчитан на начинающих инженеров данных. Они недавно изучили ClickHouse,
научится менти?** Ответ называется одной фразой до того, как писать. Не но почти не работали с ним руками. Здесь они разбирают и достраивают хранилище
небольшого интернет-магазина: проходят путь от загрузки данных до витрин и BI.
Программа дает практику общепринятых инженерных приемов. После нее участники
должны уметь построить похожее хранилище без готового образца. Состав и порядок
работы задает [программа учебных заданий](docs/specs/2026-09-01-labs-program.md).
Стенд существует только ради обучения. Его поддержка имеет смысл, когда
сохраняет возможность пройти программу или сама становится учебной задачей.
Отдельной эксплуатационной ценности у стенда нет.
Отсюда вопрос к любой доработке — коду, проверке, документу: **чему она
учит?** Ответ называется одной фразой до того, как писать. Не
складывается фраза — это вопрос владельцу, а не строчка кода: проверки в этом складывается фраза — это вопрос владельцу, а не строчка кода: проверки в этом
репозитории однажды уже переросли продукт. репозитории однажды уже переросли продукт.
**Усложнение не бесплатно, и платит за него учебная ценность.** Чем сложнее **Усложнение не бесплатно, и платит за него учебная ценность.** Чем сложнее
стенд, тем хуже он как учебный материал: внимание менти конечно, и каждая стенд, тем хуже он как учебный материал: внимание читателя конечно, и каждая
конструкция, которую он обязан расшифровать по дороге к уроку, списывается с конструкция, которую он обязан расшифровать по дороге к уроку, списывается с
этого счёта. Поэтому вопрос выше имеет вторую половину: **что менти платит, этого счёта. Поэтому вопрос выше имеет вторую половину: **что читатель платит,
чтобы это прочесть, и что получает взамен?** Ответ «вопрос — и ничего» чтобы это прочесть, и что получает взамен?** Ответ «вопрос — и ничего»
означает, что писать не нужно. означает, что писать не нужно.
@@ -157,17 +168,18 @@ Airflow) и названия из кода. Если для понятия ес
дефис (`clickstream-event.md`). Ни даты, ни номера: документ не событие дефис (`clickstream-event.md`). Ни даты, ни номера: документ не событие
истории, а текущее описание живого формата — и собирается из кода, а не истории, а текущее описание живого формата — и собирается из кода, а не
пишется руками. пишется руками.
- Имена файлов в `docs/labs/` — `S-N-slug.md`: номер ступени, номер задания - Имена файлов основной программы в `docs/labs/` — `S-N-slug.md`:
внутри ступени и слаг строчными латинскими буквами через дефис номер ступени, номер задания внутри ступени и слаг строчными латинскими
(`1-2-top-pages-daily.md`), индекс папки — `README.md`. Здесь живут ТЗ буквами через дефис (`1-2-top-pages-daily.md`). Здесь живут ТЗ
учебных заданий. Номера повторяют порядок программы из `README.md`, чтобы учебных заданий. Номера повторяют порядок программы из `README.md`, чтобы
папка читалась как оглавление; даты нет: задание — живой документ и папка читалась как оглавление; даты нет: задание — живой документ и
правится вместе с тем, о чем говорит. Слаг называет предмет задания. правится вместе с тем, о чем говорит. Слаг называет предмет задания.
Факультативы порядка не имеют и живут под одним слагом Факультативы живут в `docs/labs/optional/`, без номеров, под одним слагом
(`superset-dashboard.md`). (`superset-dashboard.md`). Общий индекс обеих частей — `docs/labs/README.md`;
отдельного индекса в `optional/` нет.
- Имена файлов в `docs/handbook/` — слаг строчными латинскими буквами через - Имена файлов в `docs/handbook/` — слаг строчными латинскими буквами через
дефис, индекс папки — `README.md`. Здесь живет путеводитель по стенду: дефис, индекс папки — `README.md`. Здесь живет путеводитель по стенду:
учебные главы для менти. Ни дат, ни номеров: глава — живой документ и учебные главы для читателя. Ни дат, ни номеров: глава — живой документ и
правится вместе с тем, что описывает. Исходники интерактивной карты лежат правится вместе с тем, что описывает. Исходники интерактивной карты лежат
подпапкой `architecture-map/`, а ее рендер — файлом `architecture-map.html` подпапкой `architecture-map/`, а ее рендер — файлом `architecture-map.html`
рядом с главами. рядом с главами.
+38 -35
View File
@@ -5,24 +5,27 @@
колонок каждой витрины задаёт раздел 7 мастер-спеки; здесь — форма, колонок каждой витрины задаёт раздел 7 мастер-спеки; здесь — форма,
наполнение и контроль. наполнение и контроль.
## Форма: по зерну, не по цене запроса ## Таблица или представление
Витрина, меняющая зерно источника — дневной агрегат, сверка по заказам, — Дневной агрегат и сверка по заказам меняют зерно источника. На этом стенде такие
материализуется: `dm.<имя>_rep` / `_dist` и публичное `dm.<имя>_v` с явным результаты хранятся в `dm.<имя>_rep` / `_dist` и читаются через публичное
списком колонок, по общим [конвенциям хранилища](storage.md). Наполняет её представление `dm.<имя>_v` с явным списком колонок. Имена и устройство объектов
`dm_transform`; ритм и механика пересборки — ниже. Витрина, сохраняющая зерно следуют общим [конвенциям хранилища](storage.md). Таблицы наполняет
источника — переименование, обогащение строки, — остаётся обычным `dm_transform`; ритм и механика пересборки описаны ниже. В задании этот путь
представлением: копию событий ради других имён колонок не держит ни один приходится пройти целиком: рассчитать агрегат, загрузить его и включить
боевой склад. обновление в конвейер.
Правило выбрано учебной целью, а не ценой запроса. На объёмах стенда Переименование колонок и обогащение строки зерно не меняют. Такие витрины на
представлениями обошлось бы всё: условия сквозь цепочку представлений стенде остаются обычными представлениями и не хранят вторую копию событий.
проходят ([исследование](../research/2026-08-22-view-predicate-pushdown.md)).
Но пересборка агрегатов конвейером — производственный паттерн, ради
которого стенд и строится, а пара «таблица рядом с представлением»
показывает: форма — решение по роли витрины, у одного слоя законны обе.
## Кто строит: стенд и менти На объёмах стенда все витрины могли бы остаться представлениями: условия
проходят сквозь их цепочку
([исследование](../research/2026-08-22-view-predicate-pushdown.md)).
Мы все равно оставили материализованные агрегаты ради практики: без них в
программе не было бы загрузки и пересборки витрин. Представления дают другой
случай: преобразование строк на прежнем зерне.
## Что готово в стенде, а что остаётся заданием
Стенд привозит готовыми витрины трёх сюжетов дашборда этапа 8 и Стенд привозит готовыми витрины трёх сюжетов дашборда этапа 8 и
инфраструктуру контроля: инфраструктуру контроля:
@@ -35,20 +38,20 @@
[картой идентичностей](dds/identity.md); [картой идентичностей](dds/identity.md);
- `dq_summary` — сводка проверок качества (ниже). - `dq_summary` — сводка проверок качества (ниже).
Остальные витрины спеки — учебные задания: менти строит их сам по ТЗ с Остальные витрины спеки нужно построить в учебных заданиях по готовому ТЗ и
образцом. Разминка — представление `events_enriched_v` (зерно события), образцу. Разминка — представление `events_enriched_v` (зерно события), затем
затем агрегаты `top_pages_daily`, `session_overview` и `dq_errors_daily`, агрегаты `top_pages_daily`, `session_overview` и `dq_errors_daily`, последней —
последней — самая содержательная `utm_effectiveness` с уроком `declared_*`. самая содержательная `utm_effectiveness` с уроком `declared_*`. В финале
Построенная витрина встаёт в конвейер руками менти: финальный пункт ТЗ каждого задания с агрегатом его задачу нужно добавить в `dm_transform`. До
каждого агрегата — добавить задачу своей витрины в `dm_transform`. До этого этого конвейер о витрине не знает, после — пересобирает её вместе со
конвейер о ней не знает и остаётся зелёным, после — витрина пересобирается стендовыми. Представление из разминки добавлять некуда: его не нужно
наравне со стендовыми. У разминочного представления такого пункта нет: пересобирать. Тексты заданий лежат в [`docs/labs/`](../labs/README.md).
пересобирать нечего. Тексты заданий — в [`docs/labs/`](../labs/README.md).
Дашборд этапа 8 стоит только на витринах стенда: приёмка этапа не зависит Дашборд этапа 8 читает только готовые витрины стенда, поэтому его приёмка не
от выполненных домашек. Свой дашборд по витринам менти — зависит от домашних заданий. Отдельный дашборд по учебным витринам можно
[необязательная лаба](../labs/superset-dashboard.md) после этапа 8. Устройство собрать в [факультативном задании](../labs/optional/superset-dashboard.md),
стендового дашборда — в [доке зоны Superset](superset.md). доступном после ступени 2. Устройство стендового дашборда описано в
[справочнике Superset](superset.md).
## Ритм пересборки ## Ритм пересборки
@@ -215,8 +218,8 @@ business_key`.
## Даг самопроверки ## Даг самопроверки
Домашки контролирует `homework_check` — отдельный даг без расписания: менти Домашние задания проверяет `homework_check` — отдельный даг без расписания,
запускает его рукой. По задаче на каждое из который запускают вручную. В нём есть задача для каждого из
[пяти заданий по витринам](../labs/README.md). [пяти заданий по витринам](../labs/README.md).
Задача красная, пока витрины нет или счёт не сходится, и зелёная, когда Задача красная, пока витрины нет или счёт не сходится, и зелёная, когда
построено верно; сообщение об ошибке говорит, что не так и куда смотреть. Проверки — инварианты против источников витрины, построено верно; сообщение об ошибке говорит, что не так и куда смотреть. Проверки — инварианты против источников витрины,
@@ -224,8 +227,8 @@ business_key`.
лабы, а не история качества конвейера. лабы, а не история качества конвейера.
Приём проверен на соседнем учебном стенде Airflow + Greenplum: Приём проверен на соседнем учебном стенде Airflow + Greenplum:
валидационный даг с группами проверок по слоям давал менти обратную связь валидационный даг с группами проверок по слоям давал обратную связь по каждому
по каждому шагу домашки, не трогая рабочий конвейер. шагу задания, не трогая рабочий конвейер.
## Отклонённые варианты ## Отклонённые варианты
@@ -233,7 +236,7 @@ business_key`.
производственный паттерн слоя — пересборка агрегатов — остался бы производственный паттерн слоя — пересборка агрегатов — остался бы
невидим, а `dm_transform` — без работы. невидим, а `dm_transform` — без работы.
- **Материализовать всё.** Копия событий ради переименования колонок — - **Материализовать всё.** Копия событий ради переименования колонок —
антипаттерн, который менти увёз бы в бой. лишняя; такой пример приучал бы без нужды дублировать исходные данные.
- **Гибрид по цене запроса.** Выбор формы по стоимости, которой на объёмах - **Гибрид по цене запроса.** Выбор формы по стоимости, которой на объёмах
стенда нет: сторож без настоящей опасности. стенда нет: сторож без настоящей опасности.
- **Проверки домашек в конвейерном звене DQ.** Конвейер стоял бы красным до - **Проверки домашек в конвейерном звене DQ.** Конвейер стоял бы красным до
@@ -242,8 +245,8 @@ business_key`.
- **«Жёлтый» статус недостроенной витрины.** У задачи Airflow нет такого - **«Жёлтый» статус недостроенной витрины.** У задачи Airflow нет такого
состояния; изображать его логами — семантика, которую пришлось бы состояния; изображать его логами — семантика, которую пришлось бы
расшифровывать. Красное «ещё не построено» честнее. расшифровывать. Красное «ещё не построено» честнее.
- **Самопроверка пишет в `dq_summary`.** Попытки менти смешались бы с - **Самопроверка пишет в `dq_summary`.** Результаты учебных попыток смешались бы
историей качества конвейера. с историей качества конвейера.
- **Окно заказов для `daily_traffic` — прошлое замерзает.** Дни за окном хранили - **Окно заказов для `daily_traffic` — прошлое замерзает.** Дни за окном хранили
бы число известных на момент закрытия — витрина тихо расходится с картой бы число известных на момент закрытия — витрина тихо расходится с картой
и протаскивает обратно «знание на дату», отвергнутое развилкой карты и протаскивает обратно «знание на дату», отвергнутое развилкой карты
+4 -3
View File
@@ -53,7 +53,7 @@
источнику. Создайте `dm.<имя>_rep` / `_dist`, донор `dm.<имя>_stage_rep` / источнику. Создайте `dm.<имя>_rep` / `_dist`, донор `dm.<имя>_stage_rep` /
`_stage_dist` и публичное `dm.<имя>_v` с явным списком колонок. При прежнем `_stage_dist` и публичное `dm.<имя>_v` с явным списком колонок. При прежнем
зерне достаточно представления: таблица дублировала бы данные. Доводы — зерне достаточно представления: таблица дублировала бы данные. Доводы —
раздел «Форма: по зерну, не по цене запроса» [справочника раздел «Таблица или представление» [справочника
DM](../architecture/dm.md). DM](../architecture/dm.md).
**Ключ шардирования — ваше решение.** Объясните, почему выбранный ключ не **Ключ шардирования — ваше решение.** Объясните, почему выбранный ключ не
@@ -122,8 +122,9 @@ DM](../architecture/dm.md).
### Superset и BI ### Superset и BI
Открывается после ступени 2. [Свой дашборд в Superset](superset-dashboard.md): Открывается после ступени 2. В задании
свои объекты BI живут отдельно от стендовых. [Свой дашборд в Superset](optional/superset-dashboard.md) вы собираете
отдельный дашборд, не меняя объекты стенда.
### Инженерия стенда ### Инженерия стенда
@@ -23,7 +23,7 @@
Не добавляйте графики в стендовый дашборд и не правьте его датасеты. При Не добавляйте графики в стендовый дашборд и не правьте его датасеты. При
инициализации Superset импорт с `--overwrite` заменит ручные правки стендовых инициализации Superset импорт с `--overwrite` заменит ручные правки стендовых
объектов файлами из репозитория. `make up` свой дашборд не стирает; срок жизни объектов файлами из репозитория. `make up` свой дашборд не стирает; срок жизни
таких объектов описан в [доке Superset](../architecture/superset.md), «Импорт таких объектов описан в [доке Superset](../../architecture/superset.md), «Импорт
при каждой инициализации». при каждой инициализации».
Заведите отдельные датасеты и графики, соберите из них свой дашборд. Заведите отдельные датасеты и графики, соберите из них свой дашборд.
+2 -2
View File
@@ -116,7 +116,7 @@
**17. Свой дашборд в Superset (готовая необязательная лаба).** **17. Свой дашборд в Superset (готовая необязательная лаба).**
Урок: пользовательские объекты BI хранятся отдельно от стендовых и переживают перезапуск. Стендовые объекты при импорте перезаписываются. Урок: пользовательские объекты BI хранятся отдельно от стендовых и переживают перезапуск. Стендовые объекты при импорте перезаписываются.
Материал: `docs/labs/superset-dashboard.md`, `docs/architecture/superset.md`. Материал: `docs/labs/optional/superset-dashboard.md`, `docs/architecture/superset.md`.\
Форма: построить свое. Форма: построить свое.
Проверка: глазами — счетного инварианта у раскладки панелей нет. Проверка: глазами — счетного инварианта у раскладки панелей нет.
@@ -276,4 +276,4 @@
STG и прием событий нельзя отдавать в форме «удали и перепиши»: код там служит несущей частью стенда. Для этих участков подходят расследования и форма «собери рядом». STG и прием событий нельзя отдавать в форме «удали и перепиши»: код там служит несущей частью стенда. Для этих участков подходят расследования и форма «собери рядом».
У всех счетных проверок остается общая дыра — раскладка по шардам. Поэтому пересчет ключа по `hostName()` должен входить в ТЗ каждого вычитания отдельной строкой. У всех счетных проверок остается общая дыра — раскладка по шардам. Поэтому пересчет ключа по `hostName()` должен входить в ТЗ каждого вычитания отдельной строкой.
+1 -1
View File
@@ -172,7 +172,7 @@
Открывается после ступени 2 основной программы. Открывается после ступени 2 основной программы.
- **Свой дашборд.** Готовая лаба - **Свой дашборд.** Готовая лаба
[`docs/labs/superset-dashboard.md`](../labs/superset-dashboard.md) учит [`docs/labs/optional/superset-dashboard.md`](../labs/optional/superset-dashboard.md) учит
хранить пользовательские объекты отдельно от стендовых. хранить пользовательские объекты отдельно от стендовых.
- **Почему дашборд не видит объект на второй ноде.** Забытый `ON CLUSTER` может - **Почему дашборд не видит объект на второй ноде.** Забытый `ON CLUSTER` может
остаться незаметным до тех пор, пока дашборд не обратится ко второй ноде. остаться незаметным до тех пор, пока дашборд не обратится ко второй ноде.