feat(generator): подключена новая модель к steady-stream сервису

- Зачем:
  - после калибровки потока и state v2 генератор нужно принять как рабочий steady-stream источник, а не как исторически сломанный прототип.
- Что:
  - добавлен сервисный тест multi-event визита с мок-публикацией во все четыре Kafka-топика.
  - compose позволяет переопределять демо-параметры генератора без правки файла, сохраняя внутренние контейнерные адреса.
  - README, OPERATIONS, KNOWN_ISSUES и карточка задачи синхронизированы с новой моделью и state v2.
- Проверка:
  - uv run --with-requirements generator/requirements.txt pytest generator/tests -q.
  - git diff --check.
  - GEN_STATE_RESET=true GEN_POPULATION_MAX=123 docker compose config.
This commit is contained in:
Dmitry Dementiev
2026-06-11 18:26:39 +03:00
parent 4a08465f7d
commit 6c4e0f4d16
6 changed files with 220 additions and 79 deletions
+33 -16
View File
@@ -1,13 +1,13 @@
# Генератор событий (MVP rev5)
> Перед использованием как источник витрин прочитать
> [KNOWN_ISSUES.md](./KNOWN_ISSUES.md): часть старого дефекта уже исправлена
> (один `click_id` на визит, путь по страницам, монотонное время, активные
> визиты между тиками), но популяция возвращающихся пользователей и
> восстановление активных визитов после рестарта ещё остаются следующими шагами.
Автономный генератор событий для Kafka с режимом `steady-stream`.
Генератор строит поток по иерархии `пользователь → визит → событие`: один
`click_id` живёт весь визит, события визита идут по страницам воронки с
монотонно растущим временем, а тиковый слой держит популяцию возвращающихся
пользователей и активные визиты между тиками. Исторический дефект старой
плоской генерации описан в [KNOWN_ISSUES.md](./KNOWN_ISSUES.md).
## Архитектура
```
@@ -29,7 +29,7 @@ generator-service -> Kafka topics -> (потребители отдельно)
| `src/clickstream_generator/intensity.py` | расчёт событийного бюджета тика |
| `src/clickstream_generator/runtime.py` | тиковый слой: активные визиты и выпуск созревших событий |
| `src/clickstream_generator/kafka_io.py` | Kafka publisher, история batch, Kafka-state и служебные топики |
| `src/clickstream_generator/state.py` | сериализуемое состояние генератора |
| `src/clickstream_generator/state.py` | сериализуемое состояние генератора v2 |
| `src/clickstream_generator/metrics.py` | Prometheus-метрики |
| `src/clickstream_generator/service.py` | основной цикл сервиса |
| `generator.py` | запуск сервиса и совместимый фасад |
@@ -79,6 +79,16 @@ generator-service -> Kafka topics -> (потребители отдельно)
| `GEN_STATE_ENABLED` | Сохранять состояние между рестартами | `true` |
| `GEN_STATE_RESET` | Сбросить состояние при старте | `false` |
В `docker-compose.yml` параметры генеративной модели и режима проброшены через
подстановку окружения, то есть их можно менять без правки файла:
```bash
GEN_LAMBDA_BASE_PER_MIN=60 GEN_POPULATION_MAX=500 docker compose up -d generator
```
Контейнерные значения `KAFKA_BOOTSTRAP_SERVERS` и `GEN_DATA_DIR` в compose
оставлены безопасными внутренними значениями `kafka:29092` и `/data`.
### Режим "раз в минуту" (для демо)
Для контролируемых демо можно установить:
@@ -197,22 +207,28 @@ docker compose exec kafka /opt/kafka/bin/kafka-console-consumer.sh \
--from-beginning
```
## State Recovery (восстановление состояния)
## Восстановление состояния
Генератор сохраняет своё состояние между перезапусками в Kafka-топик `generator_state` (compact topic). Это позволяет:
- Продолжить нумерацию тиков с места остановки (continuity)
- Сохранить последовательность случайных чисел (RNG state)
- Восстановить интенсивность генерации после рестарта
**Важно:** восстанавливается continuity по номеру тика и интенсивности, но не гарантируется отсутствие дублирования событий — `event_id` и `click_id` всегда генерируются заново (`uuid4()`).
- Восстановить популяцию пользователей
- Продолжить активные визиты после короткого простоя
### Как работает
1. После каждого успешного тика состояние сохраняется в `generator_state`
2. При старте генератор читает последнее состояние из топика
3. Если состояние найдено - продолжает с сохранённого tick
4. Если нет - начинает с tick=1
1. После каждого успешного тика состояние v2 сохраняется в `generator_state`.
2. При старте генератор читает последнее состояние из топика.
3. Если состояние найдено, сервис восстанавливает номер тика, состояние ГПСЧ,
популяцию пользователей, накопленный бюджет рождения визитов и активные
визиты.
4. Если состояния нет или оно невалидно, генератор начинает с чистого листа.
Активный визит после простоя до 30 минут продолжается со своими исходными
запланированными метками времени. Если следующий шаг визита просрочен больше
чем на 30 минут, визит закрывается без досылки остатка: для демо это выглядит
как пользователь, который ушёл, пока стенд был остановлен.
### Топик `generator_state`
@@ -250,7 +266,8 @@ docker compose exec kafka /opt/kafka/bin/kafka-topics.sh \
GEN_STATE_ENABLED=false docker compose up -d generator
```
При отключенном state management генератор всегда начинает с tick=1, RNG инициализируется с GEN_SEED (или случайно).
При отключенном сохранении состояния генератор всегда начинает с `tick=1`, а
ГПСЧ инициализируется с `GEN_SEED` или случайно.
## Тестирование