- Зачем:
- менти собирал мир генерацией (минуты и десятки минут CPU); теперь
готовый трёхдневный мир загружается импортом за ~2 минуты, и числа
у всех менти совпадают число-в-число (issue #3).
- Что:
- артефакт data/startup_history/reference-world.json.xz в git:
3 модельных дня daily-wave, ~850 МБ JSON → 32 МБ xz;
- чтение и запись артефакта понимают .xz потоково (lzma); пустое поле
artifact_path в пульте и make startup-history-import читают эталон;
- длительность профиля daily-wave стала 3d — в тон эталонному миру;
- предпроверка чистого стенда ставит зависимости генератора через uv;
экспорт и импорт разведены отдельными переменными Makefile;
- доки и runbook обновлены; новые контрактные тесты: xz round-trip
и дефолтные пути артефакта.
- Проверка:
- make test (210 + 31) и make lint зелёные; импорт на чистом стенде
за 2м03с, manifest совпал (280437 событий), Superset-проверка
зелёная; независимое ревью — APPROVED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 KiB
Редизайн пути менти: мир из артефакта и две ветки роста
Статус: принято, в работе (ветка 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)
make up— поднять стенд (единственная команда терминала).- Airflow UI:
ddl_init— создать схему. - Airflow UI:
world_init— Trigger с пустой формой. Дефолтная операция —import, путь к артефакту — дефолтом на эталонный мир. DAG сам заливает Kafka, запускает ETL и сверяет витрины. make superset-init— разовая инициализация дашборда (терминал).- Superset — готовый трёхдневный мир.
- Дальше по выбору лабы:
world_next_day(кнопка без параметров) илиmake generator-continue(живой поток).
Решения
- Эталонный мир: 3 модельных дня на профиле
daily-wave(суточная волна видна, есть «средний» день и сравнение день-к-дню). Собирает мейнтейнер один раз черезbackfill; хранится в git по фиксированному путиdata/startup_history/сжатымxz(факт 2026-07-22: ~850 МБ сырой JSON, ~32 МБ послеxz -9e). Формат не меняется:importтребуетraw_topics, сжатие снимает вопрос размера. Паттерн-образец —airflow-greenplum-solution(сидdemo.sql.xzв git, стрим-распаковка при загрузке). - Пульт переименовывается:
generator_control→world_init. Дефолт формы —import(сейчасbackfill— корень находки F3);backfillиcheckостаются в выпадашке, но уходят из учебных инструкций в runbook мейнтейнера. Список DAG'ов читается лесенкой:ddl_init→world_init→world_next_day. Цена: история прогонов старого dag_id теряется (на учебном стенде не жалко), доки и контрактный тест правятся синхронно. world_next_day— отдельный беспараметрный DAG: триггернул — день добавился.scheduleпрописан, но DAG paused иcatchup=False: процесс тяжёлый, автозапуск включается осознанно (шаг лабы), живой рост мира и так даёт веткаcontinue.- Инкрементальные счётчики manifest — предусловие расписания. Накопительное состояние счётчиков (суммы, множества uid/click_id, катящаяся контрольная сумма) переезжает в state/manifest; новый день только добавляется. Полная перечитка Kafka уходит: стоимость дня становится ~постоянной (сейчас день 2 — 638 с и растёт), риск OOM снимается. Разбор по коду — hitl-findings, F9.
- Один учебный профиль:
daily-wave(волна, ×60).ci(×1, плоско, 6h) — служебный для автотестов, менти не предлагается. Runtime-seam- проверка уже идёт наdaily-wave; перед слиянием проверить остальных потребителейci— дефолтыlaunch.pyи автотесты, где профиль зашит.
Чего здесь не делаем
- Не параллелим генерацию — ломает детерминизм (один поток ГПСЧ, переходящие визиты); корень стоимости не в ней, а в перечитке (F9).
- Не трогаем гео-фактуру — отдельная спека по ADR-0006.
- Не мигрируем лабы курса (
docs/course/) — они отстали от реальности и потребуют редизайна под три режима; отдельная работа, зафиксирована issue в трекере. - Не чиним здесь F5 (быстрый разлогин Airflow/Superset) и F4 (JS-ошибка Grid) — отдельные issues вне фичи.
- Не включаем расписание по умолчанию и не занимаемся retention сверх инкрементальных счётчиков.
- Не делаем инкрементальный ETL:
world_next_dayпо-прежнему зовётfull_refresh, его стоимость растёт с миром (на текущих объёмах — десятки секунд). Известная цена; исторический план —plans/incremental-etl-v2.md(legacy).
Форма работ
Четыре дочерних issue (GitHub, по контракту docs/agents/issue-tracker.md;
корневой issue ссылается сюда):
- Эталонный мир: сборка 3-дневного артефакта,
xz-хранение в git, стрим-распаковка наimport, дефолтный путь в форме. - Поверхность DAG'ов: переименование в
world_init, дефолтimport, выносworld_next_dayбеспараметрным DAG'ом с paused-расписанием, синхронная правка доков и контрактных тестов. - Инкрементальные счётчики manifest (без них расписание не включать).
- Один учебный профиль:
daily-wave— учебный дефолт,ci— служебный; проверка независимости runtime-seam-теста от ×1.
Зависимости: (1) и (4) сцеплены (эталонный мир собирается на daily-wave);
(3) блокирует включение расписания из (2), но не сам DAG.
Проверка
- Чистый стенд: путь менти из раздела UX проходится пустыми формами;
терминал — только
make upиmake superset-init. importэталонного мира — секунды-минуты, не десятки минут.- После
importу любого менти совпадают manifest-числа эталонного мира (события, визиты, пользователи, контрольная сумма). - Задача генерации+manifest в
world_next_dayна дне N по времени ~равна дню 1 (ETLfull_refreshрастёт с миром — это вне скоупа, см. «Чего не делаем»);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.md—backfill/сборка эталонного мира переезжают сюда как процедура мейнтейнера; дополнить про размер и хранение.CONTEXT.md— термины «мир (стенда)», «эталонный мир», «три режима менти» (добавлены этой же спекой).- Лабы
docs/course/— сознательно не здесь (см. «Чего не делаем»).