Files
clickstream-ch-kafka-supers…/docs/specs/2026-07-19-mentee-path-redesign.md
T
ddadmin d6e7a03032 docs(specs): спека редизайна пути менти — мир из артефакта и две ветки
- Зачем:
  - находки ручного HITL 2026-07-19 требовали проектного решения: путь
    менти через backfill медленный, бедный и путаный; нужна база import
    эталонного мира и две ветки роста.
- Что:
  - спека docs/specs/2026-07-19-mentee-path-redesign.md: целевая модель
    (import + next-day + continue), эталонный 3-дневный мир в git (xz),
    переименование пульта в world_init с дефолтом import, отдельный
    world_next_day, инкрементальные счётчики manifest, один учебный
    профиль; форма работ — 4 дочерних issue.
  - CONTEXT.md: термины «мир (стенда)», «эталонный мир», «три режима
    менти».
  - .scratch/hitl-findings.md восстановлен из среза 0e312b3 как рабочий
    материал фичи (до разбора в issues).
- Проверка:
  - вычитка против hitl-findings и решений обсуждения 2026-07-19.
2026-07-19 23:18:17 +03:00

12 KiB
Raw Blame History

Редизайн пути менти: мир из артефакта и две ветки роста

Статус: принято, в работе (ветка feature/mentee-path). Источник: ручной HITL-прогон пути менти 2026-07-19 — находки F1–F9 (рабочая копия — .scratch/hitl-findings.md; постоянный след — история git, срез 0e312b3). Решения приняты в обсуждении с пользователем 2026-07-19.

Проблема

Сегодняшний первый шаг менти — backfill: генерация истории с нуля. Это медленно (день модельной истории — минуты и десятки минут CPU), а результат беден (6 часов плоского профиля ci). Вокруг — ворох трения:

  • пульт generator_control подсовывает backfill дефолтом и носит имя, непонятное осваивающему стенд;
  • два профиля (ci/daily-wave) путают: менти достаётся плоский тестовый;
  • next-day спрятан в общем пульте и не годится для расписания: каждый прогон перечитывает всю историю Kafka заново — стоимость растёт квадратично по дням, память — линейно (риск OOM);
  • готового артефакта в репозитории нет: механизм import есть, а импортировать нечего.

Цели

  • Менти получает богатый мир за секунды-минуты, а не генерирует его.
  • Путь менти проходится в Airflow UI пустыми формами, без справочника.
  • У всех менти одинаковый мир: числа в лабах воспроизводимы число-в-число.
  • Мир можно растить двумя осознанными способами: пакетно по дням и живым потоком.
  • Плановый next-day перестаёт дорожать с возрастом мира.

Целевая модель

Основной режим менти — import эталонного мира. От восстановленного состояния расходятся две ветки — это три режима менти (термины — в CONTEXT.md):

  • База: import — загрузить эталонный мир из артефакта в git.
  • Ветка A: next-day — пакетно добавить следующий модельный день.
  • Ветка B: continue — живой поток от той же границы (скорость ×60).

backfill уходит мейнтейнеру: это инструмент сборки эталонного мира, не первый шаг менти (аналогия из соседнего курса Airflow: по умолчанию загружают готовый первый месяц, а не собирают его генератором).

Путь менти (UX)

  1. make up — поднять стенд (единственная команда терминала).
  2. Airflow UI: ddl_init — создать схему.
  3. Airflow UI: world_init — Trigger с пустой формой. Дефолтная операция — import, путь к артефакту — дефолтом на эталонный мир. DAG сам заливает Kafka, запускает ETL и сверяет витрины.
  4. Superset — готовый трёхдневный мир.
  5. Дальше по выбору лабы: world_next_day (кнопка без параметров) или make generator-continue (живой поток).

Решения

  1. Эталонный мир: 3 модельных дня на профиле daily-wave (суточная волна видна, есть «средний» день и сравнение день-к-дню). Собирает мейнтейнер один раз через backfill; хранится в git по фиксированному пути data/startup_history/ сжатым xz (~13 МБ; замер 2026-07-19: xz -9e жмёт артефакт в 48 раз). Формат не меняется: import требует raw_topics, сжатие снимает вопрос размера. Паттерн-образец — airflow-greenplum-solution (сид demo.sql.xz в git, стрим-распаковка при загрузке).
  2. Пульт переименовывается: generator_controlworld_init. Дефолт формы — import (сейчас backfill — корень находки F3); backfill и check остаются в выпадашке, но уходят из учебных инструкций в runbook мейнтейнера. Список DAG'ов читается лесенкой: ddl_initworld_initworld_next_day. Цена: история прогонов старого dag_id теряется (на учебном стенде не жалко), доки и контрактный тест правятся синхронно.
  3. world_next_day — отдельный беспараметрный DAG: триггернул — день добавился. schedule прописан, но DAG paused и catchup=False: процесс тяжёлый, автозапуск включается осознанно (шаг лабы), живой рост мира и так даёт ветка continue.
  4. Инкрементальные счётчики manifest — предусловие расписания. Накопительное состояние счётчиков (суммы, множества uid/click_id, катящаяся контрольная сумма) переезжает в state/manifest; новый день только добавляется. Полная перечитка Kafka уходит: стоимость дня становится ~постоянной (сейчас день 2 — 638 с и растёт), риск OOM снимается. Разбор по коду — hitl-findings, F9.
  5. Один учебный профиль: daily-wave (волна, ×60). ci (×1, плоско, 6h) — служебный для автотестов, менти не предлагается. Перед слиянием проверить, что runtime-seam-тест не завязан на скорость ×1.

Чего здесь не делаем

  • Не параллелим генерацию — ломает детерминизм (один поток ГПСЧ, переходящие визиты); корень стоимости не в ней, а в перечитке (F9).
  • Не трогаем гео-фактуру — отдельная спека по ADR-0006.
  • Не мигрируем лабы курса (docs/course/) — они отстали от реальности и потребуют редизайна под три режима; отдельная работа, зафиксирована issue в трекере.
  • Не чиним здесь F5 (быстрый разлогин Airflow/Superset) и F4 (JS-ошибка Grid) — отдельные issues вне фичи.
  • Не включаем расписание по умолчанию и не занимаемся retention сверх инкрементальных счётчиков.

Форма работ

Четыре дочерних issue (GitHub, по контракту docs/agents/issue-tracker.md; корневой issue ссылается сюда):

  1. Эталонный мир: сборка 3-дневного артефакта, xz-хранение в git, стрим-распаковка на import, дефолтный путь в форме.
  2. Поверхность DAG'ов: переименование в world_init, дефолт import, вынос world_next_day беспараметрным DAG'ом с paused-расписанием, синхронная правка доков и контрактных тестов.
  3. Инкрементальные счётчики manifest (без них расписание не включать).
  4. Один учебный профиль: daily-wave — учебный дефолт, ci — служебный; проверка независимости runtime-seam-теста от ×1.

Зависимости: (1) и (4) сцеплены (эталонный мир собирается на daily-wave); (3) блокирует включение расписания из (2), но не сам DAG.

Проверка

  • Чистый стенд: путь менти из раздела UX проходится пустыми формами; от make up до дашборда — без документации и без терминала (кроме make up).
  • import эталонного мира — секунды-минуты, не десятки минут.
  • После import у любого менти совпадают manifest-числа эталонного мира (события, визиты, пользователи, контрольная сумма).
  • world_next_day на дне N по времени ~равен дню 1; make generated-history-chain-check зелёный на стыках.
  • make test / make lint зелёные; профиль ci продолжает обслуживать автотесты.

Решения и отклонённые варианты

  • Без артефакта, просто ускорить backfill — отклонено: время менти и воспроизводимость (общий файл надёжнее «одинаковой генерации у всех»).
  • Мир «из коробки» при make up — отклонено: механизм становится невидимым, магия в учебном стенде мстит; явный import — учебная ценность и общее начало обеих лаб.
  • Расписание включённым по умолчанию — отклонено: эпизодический стенд, тяжёлый процесс, сюрпризы catchup.
  • Три тонких DAG вместо пульта — отклонено: плодит сущности; пульт с правильным дефолтом + один тонкий world_next_day достаточно.
  • git-lfs / релизные ассеты для артефакта — отклонено: после xz артефакт помещается в обычный git.

Влияние на документацию

  • README.md, docs/OPERATIONS.md, docs/ARCHITECTURE.md, docs/REPO_MAP.md — новый путь менти и имена DAG'ов (в тех же PR, что и изменения).
  • docs/runbooks/startup-history.mdbackfill/сборка эталонного мира переезжают сюда как процедура мейнтейнера; дополнить про размер и хранение.
  • CONTEXT.md — термины «мир (стенда)», «эталонный мир», «три режима менти» (добавлены этой же спекой).
  • Лабы docs/course/ — сознательно не здесь (см. «Чего не делаем»).