diff --git a/docs/specs/2026-09-01-labs-program.md b/docs/specs/2026-09-01-labs-program.md new file mode 100644 index 0000000..121a752 --- /dev/null +++ b/docs/specs/2026-09-01-labs-program.md @@ -0,0 +1,245 @@ +# Программа учебных лаб + +Эта спека собирает программу лаб из +[инвентаризации сюжетов](../research/2026-08-31-labs-inventory.md), выполненной +в тикете #201. Тикет спеки — #202, карта — #182. + +Статус: спека принята владельцем; судьба идей-кандидатов решена здесь поименно. + +## 1. Кому и зачем + +Менти уже прошел большой теоретический курс ClickHouse объемом около 60 часов. +Он знает устройство системы в теории, но почти ничего не делал своими руками. + +Поэтому в программе нет вводного блока с теорией. Здесь теория становится +ремеслом: менти руками осваивает слои хранилища, DQ, оркестрацию и идемпотентность. + +Обещание программы: менти проходит конвейер стенда автором, а не читателем. + +## 2. Устройство программы: хребет и ветки + +У частей программы два статуса, третьего нет. + +**Хребет** — обязательная часть. Менти проходит его целиком и по порядку, +потому что каждый следующий шаг опирается на предыдущий. По хребту растет +самостоятельность: от готовых ТЗ с готовыми сторожами (DQ-проверками) к заданиям, где сторожей менти строит сам. + +**Ветки** — необязательные лабы. Они собраны в тематические разделы, чтобы +менти мог одним взглядом выбрать интересное. У каждого раздела есть точка +прицепа — шаг хребта, после которого материал уже доступен. + +В программе используются четыре формы заданий: + +- *построить свое по ТЗ* — рядом есть образец, но готового ответа нет; +- *вычитание* — SQL готовой трансформации удаляется и пишется заново, а даги + остаются как несущая инфраструктура; +- *собери рядом* — менти строит свою копию результата рядом с нетронутым + стендовым и сверяет их построчно; +- *расследование* — данные уже лежат на стенде или стенд намеренно ломается, + а менти ищет причину и объясняет ее. + +## 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. Собери разбор рядом: события ODS + +Менти пакетно разбирает срез сырья событий в собственную таблицу и построчно сверяет результат с нетронутым стендовым ODS. +Урок — изучить разбор потока отдельно от живого приема и использовать стендовый результат как образец для сверки. +Матвью разбора остаются несущей стеной: их пересоздание молча теряет сообщения. + +Форма — «собери рядом». Обратная связь — построчная сверка двух разборов. Ее +границы называются прямо: срок работы ограничен окном хранения сырья, а +содержимое неопорных колонок можно сравнить только со стендовым разбором, не с +независимым эталоном. + +### Шаг 7. Мониторинг: куда смотреть + +Сначала менти знакомится с панелями стенда. Затем проводит два безопасных +расследования. В первом он находит в `query_log`, что верх списка тяжелых +запросов занимает сам мониторинг. Во втором объясняет, почему свежесть второго +источника выглядит как пила, а не как линия, и выбирает правильный слой данных +для панели. + +Форма — расследование. Обратная связь — найденные запросы и верное +объяснение формы графика и выбора слоя. Ломательные опыты остаются в ветке +«Поломки и runbook». Урок шага — куда и на что смотреть в живой системе. + +**Общее правило вычитания:** ТЗ каждого вычитания на шагах 4 и 5 включает +пересчет ключа шардирования по `hostName()`. Счетные сверки читают `_dist`, поэтому учитывают строку даже на чужом шарде +и остаются зелеными: они не видят саму ошибку раскладки. + +## 4. Ветки — пять разделов + +Ниже перечислены только названия сюжетов и их уроки. Материал, формы и способы +проверки остаются в +[инвентаризации](../research/2026-08-31-labs-inventory.md). + +### Сверка и деньги + +Эта ветка рекомендована первой среди необязательных. Точка прицепа — после +шага 1 хребта: материал живет на стенде с этапа 6. + +- **Недосчет трекера и правило «поведение — по трекеру, деньги — по бэкенду» (#186).** Разрыв воронки подтверждений показывает потерянные события и границу доверия к двум источникам. +- **Бизнес-дубль и наивная выручка (#187).** Технический и деловой дубли — разные вещи, поэтому одинаковые строки и одинаковая покупка требуют разных правил. +- **Дельта суммы, объясненная до копейки сравнением позиций.** Расхождение суммы нужно разложить до конкретной вычеркнутой позиции и объяснить до копейки. +- **Две дороги отмены и история версий.** Текущее состояние не заменяет историю, а некоторые различия после истечения срока сырья уже не восстановить. +- **Опоздавшие заказы и закрытое окно (#188).** Отчет за день становится окончательным лишь после того, как источник больше не может изменить этот день. +- **Дыхание выручки.** Внутри окна изменяемости выручка движется, а после закрытия дня фиксируется. + +### Тонкий анализ данных + +Точка прицепа — после шага 1 хребта. + +- **Склейка идентичностей во времени (#145).** Текущее знание и знание на дату дают разные отчеты; сведения из будущего меняют прошлое. +- **Справочник регионов (#143).** Менти собирает свой словарь по образцу каталога и обогащает отчет через `dictGet`. + +### Superset и BI + +Точка прицепа — после шага 2 хребта. + +- **Свой дашборд.** Готовая лаба [`docs/labs/superset-dashboard.md`](../labs/superset-dashboard.md) учит хранить пользовательские объекты отдельно от стендовых. +- **Ловушка ноды 2.** Забытый `ON CLUSTER` может остаться незаметным до тех пор, пока дашборд не обратится ко второй ноде. + +### Инженерия стенда + +Точка прицепа — после шага 1 хребта. Сюжеты про переигрывание дня и пошардовую атомарность раскроются полнее после шага 4, когда менти уже спускался в DDS. + +- **Push против pull — два способа принять топик.** Поток и слепок требуют разных способов приема и дают разную наблюдаемость. +- **Кто читал топик и куда легли данные.** Нода-потребитель может меняться, а раскладку строк определяет ключ шардирования. +- **Вторая consumer group — дубли.** Еще одна группа потребителей создает еще одну полную копию потока. +- **Дырка в офсетах.** Разрыв в офсетах показывает потерю, которую трудно заметить по самим данным. +- **Две оси времени.** Очистка сырья живет по времени загрузки, а не по модельному дню. +- **Предикат и его буквальное отрицание.** Годные строки и брак делят поток без пропусков и пересечений; менти подкладывает по строке каждого класса брака событий и находит их в `ods.event_errors_dist`. +- **Три времени и версия-наблюдение.** Версию заказа задает слепок источника, а `FINAL` скрывается за представлением. +- **Часовой пояс как способ смотреть на время.** Один момент дает разные дни в разных поясах, поэтому день и пояс нужно называть явно. +- **Сырое против разобранного (`ecommerce`).** Типизированный разбор может молча подменить ошибку значением, и его нужно сверять с источником. +- **Переиграть день X.** Конвейер сам находит дыру и определяет объем повторной обработки по состоянию хранилища. +- **Атомарность — пошардовая.** Замена дня завершается отдельно на каждом шарде, поэтому целостность результата подтверждает отдельная сверка. +- **Инкремент против пересборки (#149) — повышенная сложность.** Экономия на пересчете требует хранить историю изменений, которую полная пересборка стирает. + +### Поломки и runbook + +Точка прицепа — после шага 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 ведет работу дальше, решает тикет +#203.