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:
+53
-46
@@ -1,35 +1,41 @@
|
||||
# Генератор: известные проблемы и контекст для доработки
|
||||
|
||||
> **Статус (2026-06-06):** ветка `feature/data-generator` **не влита** в `main`.
|
||||
> Генератор **не используется** как источник данных для витрин DM/дашборда.
|
||||
> Витрины и Superset-дашборд строятся на **статическом сиде** (`data/*.jsonl`).
|
||||
> Причина — ниже. Это не «сырой код по мелочи», а концептуальный дефект
|
||||
> генеративной модели, который надо осознанно чинить перед использованием.
|
||||
> **Статус (2026-06-11):** исторический дефект старой плоской генерации закрыт
|
||||
> для режима `steady-stream`. Генератор строит визиты с общим `click_id`,
|
||||
> монотонным временем событий, путём по страницам воронки, популяцией
|
||||
> возвращающихся пользователей и состоянием v2 для активных визитов.
|
||||
>
|
||||
> **Обновление (2026-06-11):** первый срез модели визита реализован в
|
||||
> `generate_batch()`: один публичный вызов строит один `click_id` с несколькими
|
||||
> событиями, марковским путём по страницам и запланированными строго растущими
|
||||
> метками времени. Остальные пункты ниже остаются полезным историческим
|
||||
> контекстом и списком следующих шагов: популяция возвращающихся пользователей,
|
||||
> межсессионные паузы и полноценное состояние активных визитов ещё не закрыты.
|
||||
> Эта заметка больше не является предупреждением «генератор концептуально
|
||||
> сломан». Она оставлена как учебный разбор старого дефекта и как место для
|
||||
> небольших остаточных ограничений.
|
||||
|
||||
Заметка написана при дизайне Superset-дашборда (ветка
|
||||
`docs/advanced-clickstream-course`): разбирались, почему на дашборде
|
||||
`Unique Users == Unique Sessions`, и по ходу вскрылось, что генератор
|
||||
семантику сессии не чинит, а ломает сильнее. Чтобы при возвращении к
|
||||
генератору не переоткрывать это заново — фиксирую понимание целиком.
|
||||
`Unique Users == Unique Sessions`, и по ходу вскрылось, что старая реализация
|
||||
генератора на тот момент семантику сессии не чинила, а ломала сильнее. Разбор
|
||||
оставлен, чтобы не переоткрывать этот дефект заново и показать, почему новая
|
||||
модель устроена иначе.
|
||||
|
||||
## TL;DR
|
||||
|
||||
- **Модель интенсивности потока (сколько событий и когда) — нормальная.**
|
||||
Poisson по тикам + дневной коэффициент + jitter. Её можно оставить.
|
||||
- **Генеративная модель сущностей исправляется по шагам.** Срез одного визита
|
||||
уже не штампует свежий `click_id` на каждое событие, но полная иерархия
|
||||
пользователь → несколько визитов → события ещё требует популяции
|
||||
возвращающихся пользователей.
|
||||
- **Вывод:** прежде чем использовать генератор как полноценный источник,
|
||||
доделать оставшиеся уровни модели сущностей. Математику интенсивности
|
||||
трогать не обязательно.
|
||||
Poisson по тикам + дневной коэффициент + jitter сохранены.
|
||||
- **Генеративная модель сущностей переписана.** Поток больше не штампует свежий
|
||||
`click_id` на каждое событие: визит живёт несколько событий, пользователь
|
||||
может вернуться в новом визите после кулдауна.
|
||||
- **Вывод:** старый дефект `Sessions == Events` не считается актуальным
|
||||
блокером. Дальше генератор можно улучшать уже как работающую учебную модель,
|
||||
а не как концептуально сломанный источник.
|
||||
|
||||
## Текущие ограничения
|
||||
|
||||
- Тип события остаётся `pageview` для всех событий. Это осознанное ограничение:
|
||||
текущий дашборд и уроки строят воронку по `page_url_path`, а не по
|
||||
`event_type`.
|
||||
- Device/geo-профиль пользователя стабилен между визитами. Смену устройства
|
||||
генератор пока не моделирует.
|
||||
- При долгом простое больше 30 минут активный визит закрывается без досылки
|
||||
остатка. Популяция пользователей при этом сохраняется.
|
||||
|
||||
## Доменная модель (как задумано в DDL)
|
||||
|
||||
@@ -59,9 +65,9 @@ user_domain_id (постоянный пользователь, cookie)
|
||||
|
||||
Это адекватная модель *интенсивности во времени*. Претензий к ней нет.
|
||||
|
||||
## Исторический корневой дефект: модель сущностей в `generate_batch()`
|
||||
## Исторический корневой дефект: старая модель сущностей
|
||||
|
||||
До среза от 2026-06-11 прежняя реализация `generate_batch()` в монолитном
|
||||
До переработки 2026-06-11 прежняя реализация в монолитном
|
||||
`generator/generator.py` на каждое событие в батче делала примерно следующее:
|
||||
|
||||
```python
|
||||
@@ -94,33 +100,34 @@ device_event = {**base_device, "click_id": new_click_id} # user_domain_i
|
||||
| `user_domain_id` | 1:1 с `click_id` | переиспользуется (потолок ~99) |
|
||||
| Семантика | `Sessions == Users` (вырождено по пользователю) | `Sessions == Events` (сессия = одно событие) |
|
||||
|
||||
Парадокс: **статический сид как учебная основа лучше**, потому что на нём
|
||||
`click_id` несёт осмысленную семантику визита. Генератор её ломает.
|
||||
Парадокс был таким: **статический сид как учебная основа был лучше**, потому что
|
||||
на нём `click_id` нёс осмысленную семантику визита, а старый генератор её
|
||||
ломал.
|
||||
|
||||
## Что перепроверить и переделать перед использованием
|
||||
## Что сделано в новой модели
|
||||
|
||||
Чинить нужно **генеративную модель сущностей**, а не математику интенсивности:
|
||||
Чинили именно **генеративную модель сущностей**, не переписывая математику
|
||||
интенсивности:
|
||||
|
||||
1. **Иерархическая генерация вместо плоской выборки:**
|
||||
- поддерживать популяцию пользователей с *постоянным* `user_domain_id`;
|
||||
- пользователь со временем открывает 1..N **сессий** (новый `click_id` на
|
||||
сессию, с межсессионными паузами — модель «вернувшегося пользователя»);
|
||||
- сессия порождает последовательность из 1..M **событий**, разделяющих один
|
||||
`click_id` и общий device/geo, упорядоченных по времени (правдоподобный путь
|
||||
по страницам).
|
||||
2. **Распределения, требующие проверки математики:**
|
||||
- события на сессию (например, geometric/NB — длина визита);
|
||||
- сессии на пользователя за период (возвраты);
|
||||
- межсессионные интервалы (тайм-аут неактивности как граница сессии).
|
||||
3. **Время событий** внутри сессии должно расти монотонно, а не быть `now()` для
|
||||
всего батча.
|
||||
4. **`event_id`/`click_id`** уже всегда новые (`uuid4`) — при иерархической
|
||||
модели `click_id` должен переиспользоваться внутри сессии, а не на каждое
|
||||
событие (см. оговорку в `README.md`, раздел State Recovery).
|
||||
- есть ограниченная популяция пользователей с постоянным `user_domain_id`;
|
||||
- пользователь со временем открывает 1..N визитов;
|
||||
- визит порождает последовательность событий с одним `click_id`, общим
|
||||
device/geo-контекстом и путём по страницам.
|
||||
2. **Тиковый слой:**
|
||||
- событийный бюджет тика превращается в рождения визитов через среднюю длину
|
||||
визита;
|
||||
- активные визиты живут между тиками;
|
||||
- выпускаются только события, у которых наступила запланированная метка
|
||||
времени.
|
||||
3. **Время событий** внутри визита строго растёт и не прилипает к одному
|
||||
`now()` для всего батча.
|
||||
4. **Состояние v2** сохраняет популяцию, активные визиты, накопленный бюджет
|
||||
рождения визитов, номер тика и состояние ГПСЧ.
|
||||
|
||||
После такой переделки на потоке естественно получится здоровая пирамида
|
||||
`users < sessions < events`, и дашборд сможет показывать разницу
|
||||
«пользователь vs сессия» честными числами.
|
||||
После этой переделки поток на длинном окне и при штатных параметрах даёт
|
||||
здоровую пирамиду `users < sessions < events`, и дашборд может показывать
|
||||
разницу «пользователь vs сессия» честными числами.
|
||||
|
||||
## Ссылки
|
||||
|
||||
|
||||
Reference in New Issue
Block a user