feat(generator): эталонный мир в git и import по умолчанию

- Зачем:
  - менти собирал мир генерацией (минуты и десятки минут 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>
This commit is contained in:
2026-07-22 18:49:02 +03:00
co-authored by Claude Fable 5
parent b87dde79b7
commit 10ee1a1cb6
21 changed files with 165 additions and 35 deletions
+4 -3
View File
@@ -69,7 +69,8 @@ volumes или live-генератором. Для стыка backfill/live от
- `profile` — список берётся из `PROFILES` генератора;
- `duration``6h`, `2d` и т.п.; пусто означает длительность профиля;
- `seed`, `model_time_speed` — необязательные переопределения мира;
- `artifact_path` — для `backfill` путь сохранения, для `import` путь чтения;
- `artifact_path` — для `backfill` путь сохранения; для `import` путь чтения.
При пустом поле импортируется эталонный мир из репозитория;
- `expected_t_end` — необязательная ожидаемая граница перед `next-day`.
При расхождении запуск показывает ожидаемое и фактическое значения.
@@ -252,7 +253,7 @@ CHECK_LIVE_SEAM=1 GEN_LIVE_CHECK_MINUTES=10 make generated-history-check
```
Для commit gate issue 17 есть короткий runtime-путь без полного `daily-wave` на
2 суток и без Superset UI:
3 суток и без Superset UI:
```bash
make generated-history-runtime-check
@@ -270,7 +271,7 @@ make generated-history-runtime-check
LIVE_SECONDS=45 WAIT_STG_SECONDS=10 make generated-history-runtime-check
```
По умолчанию команда использует учебный профиль `daily-wave`: 2 суток с
По умолчанию команда использует учебный профиль `daily-wave`: 3 суток с
суточной волной. В live-продолжении он идёт с ×60 и тикает раз в секунду,
поэтому модельные сутки проходят примерно за 24 настенные минуты. Плоский
профиль `ci` на 6 часов остаётся служебным для автоматических тестов.
+1 -1
View File
@@ -124,7 +124,7 @@ curl -s -u admin:admin "http://localhost:3000/api/dashboards/uid/airflow-overvie
### B.1 Полная стартовая история и ETL
```bash
# История на 2 суток с видимой суточной волной
# История на 3 суток с видимой суточной волной
PROFILE=daily-wave make generated-history-analytics
make up
```
+38 -5
View File
@@ -29,11 +29,43 @@
| Профиль | Для чего | Длительность | Live-ход |
|---------|----------|--------------|----------|
| `ci` | Быстрая проверка и CI | `6h` | ×1, тик 60 с |
| `daily-wave` | История с видимой суточной волной | `2d` | ×60, тик 1 с |
| `daily-wave` | История с видимой суточной волной | `3d` | ×60, тик 1 с |
Длительность можно переопределить через `GEN_HISTORY_DURATION`, например `2d`.
Команда сама считает `GEN_MODEL_T_END` от `GEN_MODEL_T0`.
## Эталонный мир
В репозитории хранится готовый мир за три модельных дня:
`data/startup_history/reference-world.json.xz`. Это обычный JSON-артефакт,
сжатый xz. Его добавляют прямо в Git, без Git LFS.
Сопровождающий проекта собирает файл одним запуском из корня репозитория:
```bash
ARTIFACT="$PWD/data/startup_history/reference-world.json.xz" \
make startup-history-export
```
Команда выполняет backfill с профилем `daily-wave` и сразу пишет xz-файл.
Несжатый JSON занимает около 850 МБ, сжатый файл — около 32 МБ. После проверки
файл можно добавить обычной командой:
```bash
git add data/startup_history/reference-world.json.xz
```
При импорте DAG дважды читает и распаковывает артефакт: во время предпроверки и
перед записью в Kafka. Это увеличивает время импорта, но не меняет результат.
Менти в форме `generator_control` выбирает `import` и оставляет
`artifact_path` пустым. Тогда читается эталонный мир из репозитория. Из консоли
тот же импорт запускается без указания пути:
```bash
make startup-history-import
```
## Пульт в Airflow
Основной ручной путь — DAG `generator_control` в Airflow UI:
@@ -58,7 +90,8 @@
- `duration` можно оставить пустым, тогда берётся длительность профиля.
- `seed` и `model_time_speed` — необязательные переопределения мира.
- `artifact_path`: для `backfill` — куда сохранить файл; пусто — не сохранять.
Для `import` — что читать; пусто — `/opt/airflow/data/startup-history-import.json`.
Для `import` — что читать; пусто — эталонный мир из репозитория:
`/opt/airflow/data/startup_history/reference-world.json.xz`.
Backfill и import работают только на чистом стенде. Если Kafka-топики данных или
STG уже непустые, DAG упадёт до записи и подскажет `make clean`. Консольные
@@ -79,7 +112,7 @@ STG уже непустые, DAG упадёт до записи и подска
## Экспорт
По умолчанию создаётся двухсуточный артефакт `daily-wave` с суточной волной:
По умолчанию создаётся трёхсуточный артефакт `daily-wave` с суточной волной:
```bash
make startup-history-export
@@ -104,11 +137,11 @@ make clean
docker compose up -d clickhouse kafka
make ddl
ARTIFACT=/tmp/clickstream-startup-history.json make startup-history-import
make startup-history-import
sleep 10
make transform
ARTIFACT=/tmp/clickstream-startup-history.json make startup-history-check
ARTIFACT=data/startup_history/reference-world.json.xz make startup-history-check
CHECK_LIVE_SEAM=0 make generated-history-check
```
@@ -105,7 +105,7 @@ ADR-0005 решил отвязать время генератора от реа
- `reset` — подставляет `GEN_RUN_MODE=live` и `GEN_STATE_RESET=true`.
Профиль `ci` даёт быстрый 6-часовой прогон и остаётся на ×1 с тиком 60 с.
Профиль `daily-wave` даёт 2 суток, чтобы была видна суточная волна, а в live
Профиль `daily-wave` даёт 3 суток, чтобы была видна суточная волна, а в live
идёт с `GEN_MODEL_TIME_SPEED=60` и `GEN_TICK_SECONDS=1`: модельные сутки
проходят примерно за 24 настенные минуты. Пара «тик 1 с, скорость ×60» выбрана,
чтобы модельный шаг тика остался 60 секунд. Так событийный бюджет и форма волны
@@ -60,8 +60,8 @@
1. **Эталонный мир**: 3 модельных дня на профиле `daily-wave` (суточная
волна видна, есть «средний» день и сравнение день-к-дню). Собирает
мейнтейнер один раз через `backfill`; хранится в git по фиксированному
пути `data/startup_history/` сжатым `xz` (~13 МБ; замер 2026-07-19:
`xz -9e` жмёт артефакт в 48 раз). Формат не меняется: `import` требует
пути `data/startup_history/` сжатым `xz` (факт 2026-07-22: ~850 МБ
сырой JSON, ~32 МБ после `xz -9e`). Формат не меняется: `import` требует
`raw_topics`, сжатие снимает вопрос размера. Паттерн-образец —
`airflow-greenplum-solution` (сид `demo.sql.xz` в git, стрим-распаковка
при загрузке).