From eed5b4b4e67a3b2e0a4194a4b653be8d72ab00e5 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Tue, 1 Sep 2026 09:07:40 -0400 Subject: [PATCH] =?UTF-8?q?docs(specs):=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=80=D1=8C=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B0=D0=BC=D0=BC?= =?UTF-8?q?=D1=8B=20=D0=BB=D0=B0=D0=B1=20=D0=BE=D1=87=D0=B5=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=87=D0=B5=D0=BD=20=D0=BF=D0=BE=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D1=8C=D1=8E=20=D0=B2=D0=BB=D0=B0=D0=B4=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D1=86=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - термины спеки перейдут в задания, а «хребет», «ветки» и «точка прицепа» с калькированными заголовками сюжетов смущали бы студентов. - Что: - хребет → основная программа, шаги → ступени, ветки → факультативы, «точка прицепа» заменена формулой «открывается после ступени N»; - заголовки разделов и сюжетов переписаны из позиции студента («Почему выручка не сходится», «Куда попадают битые строки»); - переносы выровнены по ~80 колонок по всему файлу. - Проверка: - текст без учета пробелов не изменился на шаге переносов (SHA-256); решения, состав и порядок программы не менялись. --- docs/specs/2026-09-01-labs-program.md | 264 +++++++++++++++----------- 1 file changed, 156 insertions(+), 108 deletions(-) diff --git a/docs/specs/2026-09-01-labs-program.md b/docs/specs/2026-09-01-labs-program.md index 121a752..2c03fc4 100644 --- a/docs/specs/2026-09-01-labs-program.md +++ b/docs/specs/2026-09-01-labs-program.md @@ -12,21 +12,23 @@ Он знает устройство системы в теории, но почти ничего не делал своими руками. Поэтому в программе нет вводного блока с теорией. Здесь теория становится -ремеслом: менти руками осваивает слои хранилища, DQ, оркестрацию и идемпотентность. +ремеслом: менти руками осваивает слои хранилища, DQ, оркестрацию и +идемпотентность. Обещание программы: менти проходит конвейер стенда автором, а не читателем. -## 2. Устройство программы: хребет и ветки +## 2. Устройство программы: основная программа и факультативы У частей программы два статуса, третьего нет. -**Хребет** — обязательная часть. Менти проходит его целиком и по порядку, -потому что каждый следующий шаг опирается на предыдущий. По хребту растет -самостоятельность: от готовых ТЗ с готовыми сторожами (DQ-проверками) к заданиям, где сторожей менти строит сам. +**Основная программа** — обязательная часть. Менти проходит ее целиком и по +порядку, потому что каждая следующая ступень опирается на предыдущую. От +ступени к ступени растет самостоятельность: сначала менти получает готовые ТЗ с +готовыми сторожами (DQ-проверками), а затем строит сторожей сам. -**Ветки** — необязательные лабы. Они собраны в тематические разделы, чтобы -менти мог одним взглядом выбрать интересное. У каждого раздела есть точка -прицепа — шаг хребта, после которого материал уже доступен. +**Факультативы** — необязательные лабы. Они собраны в тематические разделы, +чтобы менти мог одним взглядом выбрать интересное. Для каждого раздела указано, +после какой ступени основной программы он открывается. В программе используются четыре формы заданий: @@ -35,43 +37,45 @@ остаются как несущая инфраструктура; - *собери рядом* — менти строит свою копию результата рядом с нетронутым стендовым и сверяет их построчно; -- *расследование* — данные уже лежат на стенде или стенд намеренно ломается, - а менти ищет причину и объясняет ее. +- *расследование* — данные уже лежат на стенде или стенд намеренно ломается, а + менти ищет причину и объясняет ее. -## 3. Хребет — семь шагов +## 3. Основная программа — семь ступеней -### Шаг 1. Витрины: пять заданий слоя DM +### Ступень 1. Построй пять витрин слоя DM -Первая часть программы опирается на шесть существующих заданий из [`docs/labs/`](../labs/README.md). -Пять ТЗ по витринам входят в первый шаг хребта и выполняются как есть. Их точечный пересмотр — работа реализации. -Шестое задание, про свой дашборд в Superset, остается необязательной лабой в ветке «Superset и BI». -Набор уроков пяти витрин уже записан в их общем описании: от слоя как договора до честных имен показателей. +Первая часть программы опирается на шесть существующих заданий из +[`docs/labs/`](../labs/README.md). Пять ТЗ по витринам входят в первую ступень +основной программы и выполняются как есть. Их точечный пересмотр — работа +реализации. Шестое задание, про свой дашборд в Superset, остается факультативом +в разделе «Superset и BI». Набор уроков пяти витрин уже записан в их общем +описании: от слоя как договора до честных имен показателей. Форма — построить свое по ТЗ. Обратную связь дает даг `homework_check`. -### Шаг 2. Свой отчет в Superset +### Ступень 2. Свой отчет в Superset -Менти строит один график в Superset поверх витрины, которую сделал на первом -шаге. Урок — увидеть, как собственный результат из хранилища становится +Менти строит один график в Superset поверх витрины, которую сделал на первой +ступени. Урок — увидеть, как собственный результат из хранилища становится отчетом в BI, не превращая знакомство с инструментом в отдельный большой проект. -Форма — построить свое по ТЗ. Результат проверяется глазами. ТЗ для этого -нового шага предстоит написать при реализации программы. Полный собственный -дашборд остается необязательной лабой в разделе «Superset и BI». +Форма — построить свое по ТЗ. Результат проверяется глазами. ТЗ для этой новой +ступени предстоит написать при реализации программы. Полный собственный дашборд +остается факультативом в разделе «Superset и BI». -### Шаг 3. Нарежь сессии сам +### Ступень 3. Нарежь сессии сам До того как открыть стендовый SQL нарезки сессий в DDS, менти самостоятельно -делит события на визиты. Сначала он сравнивает число своих сессий с эталоном `VisitID`. Затем -вручную соединяет свою сессию с эталонной и поштучно разбирает хотя бы одно -расхождение границ. Урок — счет называет проблему, но причину приходится -искать в отдельных строках. +делит события на визиты. Сначала он сравнивает число своих сессий с эталоном +`VisitID`. Затем вручную соединяет свою сессию с эталонной и поштучно разбирает +хотя бы одно расхождение границ. Урок — счет называет проблему, но причину +приходится искать в отдельных строках. Форма — построить свое по ТЗ и провести расследование. Обратная связь состоит из счетной сверки с `VisitID` и ручного сопоставления границ. -### Шаг 4. Перепиши нарезку стенда +### Ступень 4. Перепиши нарезку стенда Менти удаляет и заново пишет SQL нарезки сессий DDS: расчет объема, пересборку и замену. Урок — изменить расчетную сущность внутри готового конвейера и @@ -80,36 +84,38 @@ для ошибок, а проверка не содержит готового ответа. Форма — вычитание. Обратную связь дают `dq_check` и поштучная сверка, -построенная на третьем шаге. +построенная на третьей ступени. -### Шаг 5. Сначала сторож, потом ломай: заказы +### Ступень 5. Сначала сторож, потом ломай: заказы Сначала менти строит две недостающие DQ-проверки. Первая утверждает равенство -«цена × количество = items_total». Вторая сверяет слой с источником по дням. -Их менти строит по образцу пяти стендовых проверок в `sql/dq/`. -Обе проверки должны покраснеть на управляемой ошибке. Затем менти вручную -подкладывает по строке каждого класса брака, проверяет разбор и убирает эти строки за собой. +«цена × количество = items_total». Вторая сверяет слой с источником по дням. Их +менти строит по образцу пяти стендовых проверок в `sql/dq/`. Обе проверки +должны покраснеть на управляемой ошибке. Затем менти вручную подкладывает по +строке каждого класса брака, проверяет разбор и убирает эти строки за собой. -Только после этого он вычитает разбор заказов ODS `order_parse` и -трансформацию заказа DDS. Урок — сторожа строят до рискованного изменения, а -не после первой поломки. +Только после этого он вычитает разбор заказов ODS `order_parse` и трансформацию +заказа DDS. Урок — сторожа строят до рискованного изменения, а не после первой +поломки. Форма — построить свои проверки по ТЗ, провести управляемое расследование и выполнить вычитание. Обратную связь дают две новые проверки, баланс годных и ошибочных строк по срезу и существующие сверки заказов. -### Шаг 6. Собери разбор рядом: события ODS +### Ступень 6. Разбери сырые события и сверь результат со стендом -Менти пакетно разбирает срез сырья событий в собственную таблицу и построчно сверяет результат с нетронутым стендовым ODS. -Урок — изучить разбор потока отдельно от живого приема и использовать стендовый результат как образец для сверки. -Матвью разбора остаются несущей стеной: их пересоздание молча теряет сообщения. +Менти пакетно разбирает срез сырья событий в собственную таблицу и построчно +сверяет результат с нетронутым стендовым ODS. Урок — изучить разбор потока +отдельно от живого приема и использовать стендовый результат как образец для +сверки. Матвью разбора остаются несущей стеной: их пересоздание молча теряет +сообщения. Форма — «собери рядом». Обратная связь — построчная сверка двух разборов. Ее границы называются прямо: срок работы ограничен окном хранения сырья, а содержимое неопорных колонок можно сравнить только со стендовым разбором, не с независимым эталоном. -### Шаг 7. Мониторинг: куда смотреть +### Ступень 7. Мониторинг: куда смотреть Сначала менти знакомится с панелями стенда. Затем проводит два безопасных расследования. В первом он находит в `query_log`, что верх списка тяжелых @@ -117,80 +123,118 @@ источника выглядит как пила, а не как линия, и выбирает правильный слой данных для панели. -Форма — расследование. Обратная связь — найденные запросы и верное -объяснение формы графика и выбора слоя. Ломательные опыты остаются в ветке -«Поломки и runbook». Урок шага — куда и на что смотреть в живой системе. +Форма — расследование. Обратная связь — найденные запросы и верное объяснение +формы графика и выбора слоя. Ломательные опыты остаются в факультативе «Что +делать, когда стенд сломался». Урок ступени — куда и на что смотреть в живой +системе. -**Общее правило вычитания:** ТЗ каждого вычитания на шагах 4 и 5 включает -пересчет ключа шардирования по `hostName()`. Счетные сверки читают `_dist`, поэтому учитывают строку даже на чужом шарде -и остаются зелеными: они не видят саму ошибку раскладки. +**Общее правило вычитания:** ТЗ каждого вычитания на ступенях 4 и 5 включает +пересчет ключа шардирования по `hostName()`. Счетные сверки читают `_dist`, +поэтому учитывают строку даже на чужом шарде и остаются зелеными: они не видят +саму ошибку раскладки. -## 4. Ветки — пять разделов +## 4. Факультативы — пять разделов Ниже перечислены только названия сюжетов и их уроки. Материал, формы и способы -проверки остаются в -[инвентаризации](../research/2026-08-31-labs-inventory.md). +проверки остаются в [инвентаризации](../research/2026-08-31-labs-inventory.md). -### Сверка и деньги +### Почему выручка не сходится -Эта ветка рекомендована первой среди необязательных. Точка прицепа — после -шага 1 хребта: материал живет на стенде с этапа 6. +Этот раздел рекомендован первым из факультативов. Он открывается после ступени +1 основной программы: материал живет на стенде с этапа 6. -- **Недосчет трекера и правило «поведение — по трекеру, деньги — по бэкенду» (#186).** Разрыв воронки подтверждений показывает потерянные события и границу доверия к двум источникам. -- **Бизнес-дубль и наивная выручка (#187).** Технический и деловой дубли — разные вещи, поэтому одинаковые строки и одинаковая покупка требуют разных правил. -- **Дельта суммы, объясненная до копейки сравнением позиций.** Расхождение суммы нужно разложить до конкретной вычеркнутой позиции и объяснить до копейки. -- **Две дороги отмены и история версий.** Текущее состояние не заменяет историю, а некоторые различия после истечения срока сырья уже не восстановить. -- **Опоздавшие заказы и закрытое окно (#188).** Отчет за день становится окончательным лишь после того, как источник больше не может изменить этот день. -- **Дыхание выручки.** Внутри окна изменяемости выручка движется, а после закрытия дня фиксируется. +- **Почему трекер недосчитывает покупки (#186).** Разрыв воронки подтверждений + показывает потерянные события и границу доверия к двум источникам. +- **Почему повтор покупки завышает выручку (#187).** Движок убирает технический + дубль строки, а повтор одной покупки приходится находить по деловому ключу. +- **Какая позиция объясняет разницу в сумме.** Расхождение суммы нужно + разложить до конкретной вычеркнутой позиции и объяснить до копейки. +- **Был ли отмененный заказ оплачен.** Текущее состояние не заменяет историю, а + некоторые различия после истечения срока сырья уже не восстановить. +- **Когда отчет за день можно считать окончательным (#188).** Отчет за день + становится окончательным лишь после того, как источник больше не может + изменить этот день. +- **Почему выручка за день еще меняется.** Внутри окна изменяемости выручка + движется, а после закрытия дня фиксируется. ### Тонкий анализ данных -Точка прицепа — после шага 1 хребта. +Открывается после ступени 1 основной программы. -- **Склейка идентичностей во времени (#145).** Текущее знание и знание на дату дают разные отчеты; сведения из будущего меняют прошлое. -- **Справочник регионов (#143).** Менти собирает свой словарь по образцу каталога и обогащает отчет через `dictGet`. +- **Что мы знали о пользователе в тот день (#145).** Текущее знание и знание на + дату дают разные отчеты; сведения из будущего меняют прошлое. +- **Добавь названия регионов в отчет (#143).** Менти собирает свой словарь по + образцу каталога и обогащает отчет через `dictGet`. ### Superset и BI -Точка прицепа — после шага 2 хребта. +Открывается после ступени 2 основной программы. -- **Свой дашборд.** Готовая лаба [`docs/labs/superset-dashboard.md`](../labs/superset-dashboard.md) учит хранить пользовательские объекты отдельно от стендовых. -- **Ловушка ноды 2.** Забытый `ON CLUSTER` может остаться незаметным до тех пор, пока дашборд не обратится ко второй ноде. +- **Свой дашборд.** Готовая лаба + [`docs/labs/superset-dashboard.md`](../labs/superset-dashboard.md) учит + хранить пользовательские объекты отдельно от стендовых. +- **Почему дашборд не видит объект на второй ноде.** Забытый `ON CLUSTER` может + остаться незаметным до тех пор, пока дашборд не обратится ко второй ноде. ### Инженерия стенда -Точка прицепа — после шага 1 хребта. Сюжеты про переигрывание дня и пошардовую атомарность раскроются полнее после шага 4, когда менти уже спускался в DDS. +Открывается после ступени 1 основной программы. Сюжеты про переигрывание дня и +пошардовую атомарность раскроются полнее после ступени 4, когда менти уже +спускался в DDS. -- **Push против pull — два способа принять топик.** Поток и слепок требуют разных способов приема и дают разную наблюдаемость. -- **Кто читал топик и куда легли данные.** Нода-потребитель может меняться, а раскладку строк определяет ключ шардирования. -- **Вторая consumer group — дубли.** Еще одна группа потребителей создает еще одну полную копию потока. -- **Дырка в офсетах.** Разрыв в офсетах показывает потерю, которую трудно заметить по самим данным. -- **Две оси времени.** Очистка сырья живет по времени загрузки, а не по модельному дню. -- **Предикат и его буквальное отрицание.** Годные строки и брак делят поток без пропусков и пересечений; менти подкладывает по строке каждого класса брака событий и находит их в `ods.event_errors_dist`. -- **Три времени и версия-наблюдение.** Версию заказа задает слепок источника, а `FINAL` скрывается за представлением. -- **Часовой пояс как способ смотреть на время.** Один момент дает разные дни в разных поясах, поэтому день и пояс нужно называть явно. -- **Сырое против разобранного (`ecommerce`).** Типизированный разбор может молча подменить ошибку значением, и его нужно сверять с источником. -- **Переиграть день X.** Конвейер сам находит дыру и определяет объем повторной обработки по состоянию хранилища. -- **Атомарность — пошардовая.** Замена дня завершается отдельно на каждом шарде, поэтому целостность результата подтверждает отдельная сверка. -- **Инкремент против пересборки (#149) — повышенная сложность.** Экономия на пересчете требует хранить историю изменений, которую полная пересборка стирает. +- **Как принимать данные из Kafka: потоком или слепком.** Поток и слепок + требуют разных способов приема и дают разную наблюдаемость. +- **Почему данные попадают на свои шарды независимо от ноды чтения.** + Нода-потребитель может меняться, а раскладку строк определяет ключ + шардирования. +- **Почему вторая группа потребителей дублирует поток.** Еще одна группа + потребителей создает еще одну полную копию потока. +- **Как заметить пропущенные сообщения по офсетам.** Разрыв в офсетах + показывает потерю, которую трудно заметить по самим данным. +- **Почему сырье удаляется по времени загрузки.** Очистка сырья живет по + времени загрузки, а не по модельному дню. +- **Куда попадают битые строки.** Годные строки и брак делят поток без + пропусков и пересечений; менти подкладывает по строке каждого класса брака + событий и находит их в `ods.event_errors_dist`. +- **Какая версия заказа считается текущей.** Версию заказа задает слепок + источника, а `FINAL` скрывается за представлением. +- **Почему один момент попадает в разные дни.** Один момент дает разные дни в + разных поясах, поэтому день и пояс нужно называть явно. +- **Проверь, как стенд разобрал JSON в `ecommerce`.** Типизированный разбор + может молча подменить ошибку значением, поэтому менти сам разбирает сырой + JSON и сравнивает результат с массивами стенда. +- **Как конвейер восстанавливает пропущенный день.** Конвейер сам находит дыру + и определяет объем повторной обработки по состоянию хранилища. +- **Что будет, если день заменился не на всех шардах.** Замена дня завершается + отдельно на каждом шарде, поэтому целостность результата подтверждает + отдельная сверка. +- **Как обновлять витрину без полной пересборки (#149) — повышенная + сложность.** Экономия на пересчете требует хранить историю изменений, которую + полная пересборка стирает. -### Поломки и runbook +### Что делать, когда стенд сломался -Точка прицепа — после шага 7 хребта; весь раздел — повышенная сложность. +Открывается после ступени 7 основной программы; весь раздел — повышенная +сложность. -- **Keeper — общая память кластера.** Остановка keeper связывает состояние DDL и репликации с тем, что видно на панелях и в системных таблицах. -- **Повисший DDL.** Очередь распределенного DDL показывает путь запроса и ответ каждой ноды. +- **Что ломается вместе с keeper.** Остановка keeper связывает состояние DDL и + репликации с тем, что видно на панелях и в системных таблицах. +- **Где искать запрос DDL, который не завершился.** Очередь распределенного DDL + показывает путь запроса и ответ каждой ноды. -Итог ветки — runbook, которого сейчас не хватает стенду. Менти пишет его по +Итог раздела — runbook, которого сейчас не хватает стенду. Менти пишет его по результатам опытов, и документ остается в проекте. ## 5. Границы вычитания по потокам -Подробное сравнение потоков и таблица доказательств остаются в [инвентаризации](../research/2026-08-31-labs-inventory.md). -Спека берет это сравнение за основу и проводит свои границы. Без оговорок вычитанию отдаются только сессии DDS. -Заказы можно вычитать лишь после самодельных сторожей. Для событий ODS выбрана форма «собери рядом»; вычитание здесь отклонено. -Прием STG и забор заказов остаются несущей стеной и доступны только для расследований. -Карту идентичностей спека решает не отдавать вычитанию; причины этого выбора названы в следующем разделе. +Подробное сравнение потоков и таблица доказательств остаются в +[инвентаризации](../research/2026-08-31-labs-inventory.md). Спека берет это +сравнение за основу и проводит свои границы. Без оговорок вычитанию отдаются +только сессии DDS. Заказы можно вычитать лишь после самодельных сторожей. Для +событий ODS выбрана форма «собери рядом»; вычитание здесь отклонено. Прием STG +и забор заказов остаются несущей стеной и доступны только для расследований. +Карту идентичностей спека решает не отдавать вычитанию; причины этого выбора +названы в следующем разделе. ## 6. Что не вошло @@ -200,8 +244,8 @@ Форма «собери рядом» для карты идентичностей тоже не входит. Независимого эталона нет, а слабые инварианты проверяют допустимость связей, но не полноту. -Аналитический урок идентичности уже несет склейка во времени (#145) в ветке -«Тонкий анализ данных». +Аналитический урок идентичности уже несет склейка во времени (#145) в +факультативе «Тонкий анализ данных». Сюжеты, которые инвентаризация относит к лекциям, остаются лекциями: `Sign` и CollapsingMergeTree, топологии в бою и идентичность «как в бою». Эта спека @@ -212,16 +256,17 @@ CollapsingMergeTree, топологии в бою и идентичность « Эти решения закрывают тикеты после приемки спеки: -- #143, справочник регионов — вошла в ветку «Тонкий анализ данных». -- #145, склейка во времени — вошла в ветку «Тонкий анализ данных». -- #148, домашки ниже DM и вычитание — вошла в программу шагами 4–6 хребта. Форму для - каждого потока уточнила инвентаризация #201: вычитание, «сначала сторож» или - «собери рядом». -- #149, инкрементальный `daily_traffic` — вошла в ветку «Инженерия стенда» с - пометкой «повышенная сложность». -- #186, воронка подтверждений — вошла в ветку «Сверка и деньги». -- #187, бизнес-дубль — вошла в ветку «Сверка и деньги». -- #188, опоздавшие заказы — вошла в ветку «Сверка и деньги». +- #143, справочник регионов — вошла в факультатив «Тонкий анализ данных». +- #145, склейка во времени — вошла в факультатив «Тонкий анализ данных». +- #148, домашки ниже DM и вычитание — вошла в основную программу как ступени + 4–6. Форму для каждого потока уточнила инвентаризация #201: вычитание, + «сначала сторож» или «собери рядом». +- #149, инкрементальный `daily_traffic` — вошла в факультатив «Инженерия + стенда» с пометкой «повышенная сложность». +- #186, воронка подтверждений — вошла в факультатив «Почему выручка не + сходится». +- #187, бизнес-дубль — вошла в факультатив «Почему выручка не сходится». +- #188, опоздавшие заказы — вошла в факультатив «Почему выручка не сходится». ## 8. Отклоненные варианты оси @@ -231,15 +276,18 @@ CollapsingMergeTree, топологии в бою и идентичность « менти еще не пользовался. - **Два прохода: сначала расследования снизу вверх, потом постройка сверху вниз.** Пролог из восьми расследований откладывает первый собственный - результат. Вместо этого расследования разнесены по веткам и доступны в - своих точках прицепа. -- **Сюжетами поперек слоев.** Такой порядок ломает принятое решение, по которому готовые задания открывают программу, и превращает путь в сетку зависимостей. + результат. Вместо этого расследования разнесены по факультативам и + открываются по мере прохождения основной программы. +- **Сюжетами поперек слоев.** Такой порядок ломает принятое решение, по + которому готовые задания открывают программу, и превращает путь в сетку + зависимостей. ## 9. Границы спеки и что дальше -Спека не переписывает существующие ТЗ в `docs/labs/` и общий README; точечный пересмотр относится к реализации. +Спека не переписывает существующие ТЗ в `docs/labs/` и общий README; точечный +пересмотр относится к реализации. Реализация программы тоже находится за границей спеки. В нее входят новые ТЗ -для шагов 2–7 хребта и веток, а также отражение порядка программы в -`docs/labs/README.md`. Докуда карта #182 ведет работу дальше, решает тикет -#203. +для ступеней 2–7 основной программы и факультативов, а также отражение порядка +программы в `docs/labs/README.md`. Докуда карта #182 ведет работу дальше, +решает тикет #203.