feat(generator): добавлен артефакт стартовой истории
- Зачем: - чистый стенд должен восстанавливать стартовую историю без повторной генерации. - Что: - добавлены export/import артефакта через Kafka и compact-топики. - добавлена manifest-aware сверка ClickHouse и защита от смешения state. - добавлен runbook использования стартовой истории. - Проверка: - uv run --with-requirements generator/requirements.txt pytest generator/tests -q. - bash -n scripts/check_startup_history_manifest.sh scripts/export_startup_history_artifact.sh scripts/import_startup_history_artifact.sh. - git diff --check.
This commit is contained in:
@@ -118,6 +118,7 @@ make generator-logs
|
||||
| `GEN_MODEL_TIMEZONE` | Часовой пояс модельных часов для дневного коэффициента | `UTC` |
|
||||
| `GEN_MODEL_TIME_SPEED` | Сколько модельных секунд проходит за одну настенную секунду | `1` |
|
||||
| `GEN_RUN_MODE` | Режим генератора | `live` |
|
||||
| `GEN_STARTUP_HISTORY_ARTIFACT` | JSON-файл для экспорта стартовой истории в режиме `backfill` | пусто |
|
||||
| `GEN_STATE_ENABLED` | Сохранять state v2 между рестартами | `true` |
|
||||
| `GEN_STATE_RESET` | Сбросить state при старте | `false` |
|
||||
|
||||
@@ -163,6 +164,9 @@ GEN_MODEL_T_END=2026-01-02T00:00:00+00:00 make generated-history-analytics
|
||||
контрольными числами. При live-запуске с теми же настройками генератор видит,
|
||||
что state совпадает с manifest, и стартует ровно с `T_end` без настенной дельты.
|
||||
|
||||
Если историю нужно сохранить в файл и восстановить на чистом стенде без новой
|
||||
генерации, используйте [runbook стартовой истории](./runbooks/startup-history.md).
|
||||
|
||||
Для чистого повтора пересоздавайте volumes. Это сбрасывает ClickHouse,
|
||||
Kafka-топики данных, state и manifest генератора. `make generated-history-analytics`
|
||||
делает это по умолчанию (`CLEAN_START=1`).
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Runbook: стартовая история стенда
|
||||
|
||||
Этот runbook нужен, чтобы один раз создать стартовую историю генератора, сохранить
|
||||
её в файл и быстро восстановить на чистом стенде.
|
||||
|
||||
За устройством генератора см. [`generator/README.md`](../../generator/README.md)
|
||||
и [спеку модельного времени](../specs/2026-06-14-generator-model-time-and-startup-history.md).
|
||||
|
||||
## Что дёшево
|
||||
|
||||
- Перезапуск без очистки volumes: Kafka хранит `generator_state` и manifest.
|
||||
- Live-продолжение после импорта: генератор стартует с `T_end`, если настройки
|
||||
совпадают.
|
||||
- Восстановление чистого стенда из готового файла: события повторно пишутся в
|
||||
Kafka, ClickHouse наполняется штатным путём.
|
||||
|
||||
## Что требует нового артефакта
|
||||
|
||||
- Другая длительность истории (`GEN_MODEL_T_END`).
|
||||
- Другой `GEN_SEED`, `GEN_MODEL_T0`, часовой пояс, скорость или настройки
|
||||
генерации.
|
||||
- Осознанный новый мир после несовместимого state: сначала сбросьте state через
|
||||
`GEN_STATE_RESET=true` или чистые volumes.
|
||||
|
||||
## Экспорт
|
||||
|
||||
По умолчанию создаётся быстрый 6-часовой артефакт:
|
||||
|
||||
```bash
|
||||
make startup-history-export
|
||||
```
|
||||
|
||||
Файл по умолчанию: `/tmp/clickstream-startup-history.json`.
|
||||
|
||||
Суточная история:
|
||||
|
||||
```bash
|
||||
ARTIFACT=/tmp/clickstream-startup-history-1d.json \
|
||||
GEN_MODEL_T_END=2026-01-02T00:00:00+00:00 \
|
||||
make startup-history-export
|
||||
```
|
||||
|
||||
Команда делает чистый backfill и пишет в файл один связный набор: события Kafka,
|
||||
state и manifest.
|
||||
|
||||
## Импорт на чистый стенд
|
||||
|
||||
```bash
|
||||
make clean
|
||||
docker compose up -d clickhouse kafka
|
||||
make ddl
|
||||
|
||||
ARTIFACT=/tmp/clickstream-startup-history.json make startup-history-import
|
||||
|
||||
sleep 10
|
||||
make transform
|
||||
ARTIFACT=/tmp/clickstream-startup-history.json make startup-history-check
|
||||
make generated-history-check
|
||||
```
|
||||
|
||||
Импорт не пишет напрямую в ClickHouse. Он воспроизводит события и служебные
|
||||
compact-топики в Kafka. ClickHouse читает данные через свои Kafka-таблицы и
|
||||
Materialized View, затем batch строит ODS, DDS и DM.
|
||||
|
||||
`make startup-history-check` сверяет контрольные числа DM-витрины с manifest
|
||||
артефакта: события, визиты, пользователей и диапазон `event_timestamp`. Если
|
||||
data-топики Kafka уже непустые, импорт остановится до публикации событий.
|
||||
|
||||
В текущем стеке `kafka-python` не даёт транзакционный producer для нескольких
|
||||
топиков. Поэтому импорт остаётся clean-stand операцией: при ошибке записи он
|
||||
удаляет import-топики Kafka, чтобы повторный импорт не дописал дубли. Если
|
||||
ClickHouse уже успел прочитать частичные сообщения, очистите стенд через
|
||||
`make clean` и повторите импорт.
|
||||
|
||||
## Live-продолжение
|
||||
|
||||
После импорта запускайте live с теми же настройками, что были в артефакте:
|
||||
|
||||
```bash
|
||||
GEN_STATE_RESET=false \
|
||||
GEN_SEED=4242 \
|
||||
GEN_MODEL_T0=2026-01-01T00:00:00+00:00 \
|
||||
GEN_MODEL_T_END=2026-01-01T06:00:00+00:00 \
|
||||
GEN_MODEL_TIMEZONE=UTC \
|
||||
GEN_MODEL_TIME_SPEED=1 \
|
||||
docker compose up -d generator
|
||||
```
|
||||
|
||||
Если читаемый state есть, но настройки не совпадают, генератор падает с перечнем
|
||||
полей. Это защита от смешения разных миров. Для намеренного нового мира
|
||||
используйте `GEN_STATE_RESET=true` или `make clean`.
|
||||
@@ -90,6 +90,8 @@ ADR-0005 решил отвязать время генератора от реа
|
||||
одну настенную секунду. Формат: положительное число, по умолчанию `1`.
|
||||
- `GEN_RUN_MODE` — режим запуска: `live` или `backfill`. Значение по умолчанию —
|
||||
`live`.
|
||||
- `GEN_STARTUP_HISTORY_ARTIFACT` — путь к JSON-файлу, куда `backfill` дополнительно
|
||||
пишет портативный артефакт стартовой истории. В обычном live-запуске не нужен.
|
||||
- `GEN_SEED`, `GEN_TICK_SECONDS` и остальные настройки генерации остаются частью
|
||||
контракта повторяемости. Если они отличаются, артефакт стартовой истории
|
||||
считается другим.
|
||||
@@ -162,12 +164,35 @@ resume_model_at =
|
||||
`GEN_MODEL_TIME_SPEED` короткая настенная пауза может стать долгой модельной
|
||||
паузой, и тогда просроченные активные визиты закрываются.
|
||||
|
||||
Если state отсутствует или повреждён, генератор стартует чисто и пишет
|
||||
предупреждение. Если state читается, `GEN_STATE_RESET=false`, но настройки
|
||||
продолжения несовместимы (`GEN_SEED`, `GEN_MODEL_T0`, `GEN_MODEL_TIMEZONE`,
|
||||
`GEN_MODEL_TIME_SPEED`, а для стартовой истории ещё и `GEN_MODEL_T_END` или
|
||||
manifest), генератор должен упасть с перечнем разошедшихся полей и подсказкой
|
||||
использовать `GEN_STATE_RESET=true` для осознанного нового мира.
|
||||
|
||||
#### Манифест стартовой истории
|
||||
|
||||
Стартовая история состоит из трёх частей: события, слепок состояния и манифест.
|
||||
Манифест хранится как JSON в Kafka compact-topic
|
||||
В рабочем стенде манифест хранится как JSON в Kafka compact-topic
|
||||
`generator_startup_history_manifest`, ключ `default`. Слепок состояния хранится
|
||||
в `generator_state`, ключ `default`. Манифест минимум содержит:
|
||||
в `generator_state`, ключ `default`.
|
||||
|
||||
Портативный файл-артефакт хранит тот же связный набор: сообщения топиков
|
||||
`browser_events`, `location_events`, `device_events`, `geo_events`, слепок state
|
||||
и manifest. Для событий хранится raw JSON value, чтобы импорт мог воспроизвести
|
||||
Kafka-сообщения без повторной сериализации dict. Импорт артефакта воспроизводит
|
||||
сообщения в Kafka и записывает state с manifest в служебные compact-топики.
|
||||
Напрямую в ClickHouse импорт не пишет: ClickHouse наполняется штатным путём через
|
||||
Kafka engine и Materialized View.
|
||||
|
||||
Импорт рассчитан на чистый стенд. Перед записью он проверяет, что data-топики
|
||||
Kafka пустые. Так как текущий Python-клиент Kafka не даёт транзакционный producer
|
||||
для нескольких топиков, при ошибке записи импорт удаляет import-топики Kafka,
|
||||
чтобы повторный импорт не дописал дубли; если ClickHouse уже успел прочитать
|
||||
частичные сообщения, стенд очищается как clean-stand сценарий.
|
||||
|
||||
Манифест минимум содержит:
|
||||
|
||||
- `manifest_version`;
|
||||
- `generated_at` — настенная UTC-метка создания артефакта;
|
||||
@@ -186,8 +211,8 @@ resume_model_at =
|
||||
только если `state.last_batch_id`, `state.model_timestamp`, `GEN_SEED`,
|
||||
`GEN_MODEL_T0`, `GEN_MODEL_T_END`, `GEN_MODEL_TIMEZONE`, `GEN_MODEL_TIME_SPEED`
|
||||
и `generation_settings` совпадают. Startup-history state без подходящего
|
||||
manifest считается несовместимым и ведёт к чистому старту, а не к восстановлению
|
||||
по правилу live-сбоя.
|
||||
manifest считается несовместимым читаемым state и даёт жёсткий отказ при
|
||||
`GEN_STATE_RESET=false`.
|
||||
|
||||
#### Повторяемая проверка в ClickHouse
|
||||
|
||||
|
||||
Reference in New Issue
Block a user