diff --git a/AGENTS.md b/AGENTS.md index 5d97c7b..4b11367 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,15 +8,26 @@ Стенд строится по спеке [«Боевой реализм стенда (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`). Ни даты, ни номера: документ не событие истории, а текущее описание живого формата — и собирается из кода, а не пишется руками. -- Имена файлов в `docs/labs/` — `S-N-slug.md`: номер ступени, номер задания - внутри ступени и слаг строчными латинскими буквами через дефис - (`1-2-top-pages-daily.md`), индекс папки — `README.md`. Здесь живут ТЗ +- Имена файлов основной программы в `docs/labs/` — `S-N-slug.md`: + номер ступени, номер задания внутри ступени и слаг строчными латинскими + буквами через дефис (`1-2-top-pages-daily.md`). Здесь живут ТЗ учебных заданий. Номера повторяют порядок программы из `README.md`, чтобы папка читалась как оглавление; даты нет: задание — живой документ и правится вместе с тем, о чем говорит. Слаг называет предмет задания. - Факультативы порядка не имеют и живут под одним слагом - (`superset-dashboard.md`). + Факультативы живут в `docs/labs/optional/`, без номеров, под одним слагом + (`superset-dashboard.md`). Общий индекс обеих частей — `docs/labs/README.md`; + отдельного индекса в `optional/` нет. - Имена файлов в `docs/handbook/` — слаг строчными латинскими буквами через дефис, индекс папки — `README.md`. Здесь живет путеводитель по стенду: - учебные главы для менти. Ни дат, ни номеров: глава — живой документ и + учебные главы для читателя. Ни дат, ни номеров: глава — живой документ и правится вместе с тем, что описывает. Исходники интерактивной карты лежат подпапкой `architecture-map/`, а ее рендер — файлом `architecture-map.html` рядом с главами. diff --git a/docs/architecture/dm.md b/docs/architecture/dm.md index 452a68a..4dc98f7 100644 --- a/docs/architecture/dm.md +++ b/docs/architecture/dm.md @@ -5,24 +5,27 @@ колонок каждой витрины задаёт раздел 7 мастер-спеки; здесь — форма, наполнение и контроль. -## Форма: по зерну, не по цене запроса +## Таблица или представление -Витрина, меняющая зерно источника — дневной агрегат, сверка по заказам, — -материализуется: `dm.<имя>_rep` / `_dist` и публичное `dm.<имя>_v` с явным -списком колонок, по общим [конвенциям хранилища](storage.md). Наполняет её -`dm_transform`; ритм и механика пересборки — ниже. Витрина, сохраняющая зерно -источника — переименование, обогащение строки, — остаётся обычным -представлением: копию событий ради других имён колонок не держит ни один -боевой склад. +Дневной агрегат и сверка по заказам меняют зерно источника. На этом стенде такие +результаты хранятся в `dm.<имя>_rep` / `_dist` и читаются через публичное +представление `dm.<имя>_v` с явным списком колонок. Имена и устройство объектов +следуют общим [конвенциям хранилища](storage.md). Таблицы наполняет +`dm_transform`; ритм и механика пересборки описаны ниже. В задании этот путь +приходится пройти целиком: рассчитать агрегат, загрузить его и включить +обновление в конвейер. -Правило выбрано учебной целью, а не ценой запроса. На объёмах стенда -представлениями обошлось бы всё: условия сквозь цепочку представлений -проходят ([исследование](../research/2026-08-22-view-predicate-pushdown.md)). -Но пересборка агрегатов конвейером — производственный паттерн, ради -которого стенд и строится, а пара «таблица рядом с представлением» -показывает: форма — решение по роли витрины, у одного слоя законны обе. +Переименование колонок и обогащение строки зерно не меняют. Такие витрины на +стенде остаются обычными представлениями и не хранят вторую копию событий. -## Кто строит: стенд и менти +На объёмах стенда все витрины могли бы остаться представлениями: условия +проходят сквозь их цепочку +([исследование](../research/2026-08-22-view-predicate-pushdown.md)). +Мы все равно оставили материализованные агрегаты ради практики: без них в +программе не было бы загрузки и пересборки витрин. Представления дают другой +случай: преобразование строк на прежнем зерне. + +## Что готово в стенде, а что остаётся заданием Стенд привозит готовыми витрины трёх сюжетов дашборда этапа 8 и инфраструктуру контроля: @@ -35,20 +38,20 @@ [картой идентичностей](dds/identity.md); - `dq_summary` — сводка проверок качества (ниже). -Остальные витрины спеки — учебные задания: менти строит их сам по ТЗ с -образцом. Разминка — представление `events_enriched_v` (зерно события), -затем агрегаты `top_pages_daily`, `session_overview` и `dq_errors_daily`, -последней — самая содержательная `utm_effectiveness` с уроком `declared_*`. -Построенная витрина встаёт в конвейер руками менти: финальный пункт ТЗ -каждого агрегата — добавить задачу своей витрины в `dm_transform`. До этого -конвейер о ней не знает и остаётся зелёным, после — витрина пересобирается -наравне со стендовыми. У разминочного представления такого пункта нет: -пересобирать нечего. Тексты заданий — в [`docs/labs/`](../labs/README.md). +Остальные витрины спеки нужно построить в учебных заданиях по готовому ТЗ и +образцу. Разминка — представление `events_enriched_v` (зерно события), затем +агрегаты `top_pages_daily`, `session_overview` и `dq_errors_daily`, последней — +самая содержательная `utm_effectiveness` с уроком `declared_*`. В финале +каждого задания с агрегатом его задачу нужно добавить в `dm_transform`. До +этого конвейер о витрине не знает, после — пересобирает её вместе со +стендовыми. Представление из разминки добавлять некуда: его не нужно +пересобирать. Тексты заданий лежат в [`docs/labs/`](../labs/README.md). -Дашборд этапа 8 стоит только на витринах стенда: приёмка этапа не зависит -от выполненных домашек. Свой дашборд по витринам менти — -[необязательная лаба](../labs/superset-dashboard.md) после этапа 8. Устройство -стендового дашборда — в [доке зоны Superset](superset.md). +Дашборд этапа 8 читает только готовые витрины стенда, поэтому его приёмка не +зависит от домашних заданий. Отдельный дашборд по учебным витринам можно +собрать в [факультативном задании](../labs/optional/superset-dashboard.md), +доступном после ступени 2. Устройство стендового дашборда описано в +[справочнике Superset](superset.md). ## Ритм пересборки @@ -215,8 +218,8 @@ business_key`. ## Даг самопроверки -Домашки контролирует `homework_check` — отдельный даг без расписания: менти -запускает его рукой. По задаче на каждое из +Домашние задания проверяет `homework_check` — отдельный даг без расписания, +который запускают вручную. В нём есть задача для каждого из [пяти заданий по витринам](../labs/README.md). Задача красная, пока витрины нет или счёт не сходится, и зелёная, когда построено верно; сообщение об ошибке говорит, что не так и куда смотреть. Проверки — инварианты против источников витрины, @@ -224,8 +227,8 @@ business_key`. лабы, а не история качества конвейера. Приём проверен на соседнем учебном стенде Airflow + Greenplum: -валидационный даг с группами проверок по слоям давал менти обратную связь -по каждому шагу домашки, не трогая рабочий конвейер. +валидационный даг с группами проверок по слоям давал обратную связь по каждому +шагу задания, не трогая рабочий конвейер. ## Отклонённые варианты @@ -233,7 +236,7 @@ business_key`. производственный паттерн слоя — пересборка агрегатов — остался бы невидим, а `dm_transform` — без работы. - **Материализовать всё.** Копия событий ради переименования колонок — - антипаттерн, который менти увёз бы в бой. + лишняя; такой пример приучал бы без нужды дублировать исходные данные. - **Гибрид по цене запроса.** Выбор формы по стоимости, которой на объёмах стенда нет: сторож без настоящей опасности. - **Проверки домашек в конвейерном звене DQ.** Конвейер стоял бы красным до @@ -242,8 +245,8 @@ business_key`. - **«Жёлтый» статус недостроенной витрины.** У задачи Airflow нет такого состояния; изображать его логами — семантика, которую пришлось бы расшифровывать. Красное «ещё не построено» честнее. -- **Самопроверка пишет в `dq_summary`.** Попытки менти смешались бы с - историей качества конвейера. +- **Самопроверка пишет в `dq_summary`.** Результаты учебных попыток смешались бы + с историей качества конвейера. - **Окно заказов для `daily_traffic` — прошлое замерзает.** Дни за окном хранили бы число известных на момент закрытия — витрина тихо расходится с картой и протаскивает обратно «знание на дату», отвергнутое развилкой карты diff --git a/docs/labs/README.md b/docs/labs/README.md index 46e2315..b3316e1 100644 --- a/docs/labs/README.md +++ b/docs/labs/README.md @@ -53,7 +53,7 @@ источнику. Создайте `dm.<имя>_rep` / `_dist`, донор `dm.<имя>_stage_rep` / `_stage_dist` и публичное `dm.<имя>_v` с явным списком колонок. При прежнем зерне достаточно представления: таблица дублировала бы данные. Доводы — -раздел «Форма: по зерну, не по цене запроса» [справочника +раздел «Таблица или представление» [справочника DM](../architecture/dm.md). **Ключ шардирования — ваше решение.** Объясните, почему выбранный ключ не @@ -122,8 +122,9 @@ DM](../architecture/dm.md). ### Superset и BI -Открывается после ступени 2. [Свой дашборд в Superset](superset-dashboard.md): -свои объекты BI живут отдельно от стендовых. +Открывается после ступени 2. В задании +[Свой дашборд в Superset](optional/superset-dashboard.md) вы собираете +отдельный дашборд, не меняя объекты стенда. ### Инженерия стенда diff --git a/docs/labs/superset-dashboard.md b/docs/labs/optional/superset-dashboard.md similarity index 99% rename from docs/labs/superset-dashboard.md rename to docs/labs/optional/superset-dashboard.md index d3081f4..c488cbb 100644 --- a/docs/labs/superset-dashboard.md +++ b/docs/labs/optional/superset-dashboard.md @@ -23,7 +23,7 @@ Не добавляйте графики в стендовый дашборд и не правьте его датасеты. При инициализации Superset импорт с `--overwrite` заменит ручные правки стендовых объектов файлами из репозитория. `make up` свой дашборд не стирает; срок жизни -таких объектов описан в [доке Superset](../architecture/superset.md), «Импорт +таких объектов описан в [доке Superset](../../architecture/superset.md), «Импорт при каждой инициализации». Заведите отдельные датасеты и графики, соберите из них свой дашборд. diff --git a/docs/research/2026-08-31-labs-inventory.md b/docs/research/2026-08-31-labs-inventory.md index e4bce60..2204dbb 100644 --- a/docs/research/2026-08-31-labs-inventory.md +++ b/docs/research/2026-08-31-labs-inventory.md @@ -116,7 +116,7 @@ **17. Свой дашборд в Superset (готовая необязательная лаба).** Урок: пользовательские объекты 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 и прием событий нельзя отдавать в форме «удали и перепиши»: код там служит несущей частью стенда. Для этих участков подходят расследования и форма «собери рядом». -У всех счетных проверок остается общая дыра — раскладка по шардам. Поэтому пересчет ключа по `hostName()` должен входить в ТЗ каждого вычитания отдельной строкой. \ No newline at end of file +У всех счетных проверок остается общая дыра — раскладка по шардам. Поэтому пересчет ключа по `hostName()` должен входить в ТЗ каждого вычитания отдельной строкой. diff --git a/docs/specs/2026-09-01-labs-program.md b/docs/specs/2026-09-01-labs-program.md index a0380b8..424b87d 100644 --- a/docs/specs/2026-09-01-labs-program.md +++ b/docs/specs/2026-09-01-labs-program.md @@ -172,7 +172,7 @@ Открывается после ступени 2 основной программы. - **Свой дашборд.** Готовая лаба - [`docs/labs/superset-dashboard.md`](../labs/superset-dashboard.md) учит + [`docs/labs/optional/superset-dashboard.md`](../labs/optional/superset-dashboard.md) учит хранить пользовательские объекты отдельно от стендовых. - **Почему дашборд не видит объект на второй ноде.** Забытый `ON CLUSTER` может остаться незаметным до тех пор, пока дашборд не обратится ко второй ноде.