Files
clickstream-ch-kafka-supers…/CONTEXT.md
T
Dmitry Dementiev 341637c810 docs(generator): зафиксирована модель времени генератора (ADR-0005)
- Зачем:
  - инвариант «время генератора ≡ реальное ×1» неудобен для учебного плана:
    медленные явления (возвраты, воронка) не успеть показать на уроке, а
    историческую глубину живой генератор не создаёт.
- Что:
  - добавлен ADR-0005: модельные часы отвязаны от настенного времени, режимы
    (живой ×1 / ускоренный ×K / заливка) — драйверы одного шва, правило 30 минут
    переопределено в модельном времени; сид-продолжение помечено как будущее.
  - в CONTEXT.md разведены три значения «сида» и добавлен термин модельного
    времени и масштаба ×K.
  - добавлен handoff с отложенным ревью петли и реконсиляцией мат-спеки.
- Проверка:
  - прочитать docs/adr/0005-generator-model-clock.md и раздел «Три значения
    слова сид» в CONTEXT.md; git log -1.
2026-06-11 18:37:27 +03:00

136 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CONTEXT.md — глоссарий домена
Доменный язык стенда: как мы называем сущности кликстрима и что под ними
понимаем. Один термин — одно имя. Ведётся вместе с `docs/adr/` (см.
[ADR-0001](./docs/adr/0001-spec-adr-issue-layout.md)).
## Сущности
### Пользователь (user)
`user_domain_id` (`UUID`) — постоянный идентификатор пользователя (cookie-уровень),
переживает отдельные визиты. Источник — `device_events`, привязан к `click_id`.
В витринах: `uniqExact(user_domain_id)`.
### Визит / сессия (visit / session)
`click_id` (`UUID`) — **визит**: envelope из одного и более событий с общим
контекстом device/geo. В DDL `dds.click` описана как «контекст сессии
пользователя» (см. [`sql/ddl/dds/30_dds.sql`](./sql/ddl/dds/30_dds.sql)).
Важно: в источнике **нет отдельного session-таймаута** (как Snowplow
`domain_sessionid`). Поэтому в этой модели **сессия ≡ визит ≡ `click_id`**
группа событий под одним идентификатором, а не окно активности по тайм-ауту.
Если в будущем понадобится «настоящая» сессия по 30-минутному окну неактивности —
это отдельная производная над `user_domain_id` + `event_ts`, и на сиде она дала
бы **другие** группы: в полном сиде 11 внутрисессионных пауз превышают 30 минут,
а разброс времени внутри `click_id` доходит до 49 минут (см. профиль
сид-датасета ниже; ранее здесь ошибочно значилось «разброс ≤ 1 мин» — это был
замер по малому срезу файла).
«Сессию» и «визит» используем как синонимы; в UI предпочитаем «визит», когда важно
подчеркнуть, что это не сессия-по-тайм-ауту.
### Событие (event)
`event_id` (`UUID`) — отдельное действие пользователя в рамках визита: `pageview`,
`click`, `purchase`, `add_to_cart` и т.п. (поле `event_type`). Источник —
`browser_events` + `location_events`. В витринах: `count()`.
## Иерархия
```
user_domain_id (пользователь, постоянный)
└── click_id (визит/сессия — 1..N событий с общим device/geo)
└── event_id (событие: pageview / click / purchase ...)
```
Здоровые данные дают пирамиду `users ≤ sessions ≤ events`.
## Термины потоковой генерации (steady-stream)
Относятся к синтетическому потоку из генератора (режим `steady-stream`), не к
статическому сиду. Решение и контекст — [ADR-0004](./docs/adr/0004-steady-stream-synthetic-generator.md),
форма доработки — [спека генератора](./docs/specs/2026-06-09-generator-rework-hierarchical.md).
### Популяция пользователей (user population)
Множество пользователей с **постоянными** `user_domain_id`, которые генератор
держит во времени и из которых разыгрывает активность. В отличие от сида (где
пул `user_domain_id` плоский и 1:1 с визитами), популяция — это источник
возвратов: один и тот же пользователь порождает несколько визитов.
### Возвращающийся пользователь (returning user)
Пользователь, открывающий **более одного** визита (`click_id`) во времени, с
межсессионными паузами. Именно возвраты дают расхождение `users < sessions`
то, чего нет на сиде (`users == sessions`) и что отличает поток от статики.
### Три значения слова «сид»
Слово перегружено — в разговоре про генератор это **три разные сущности**, их
нельзя путать (см. [ADR-0005](./docs/adr/0005-generator-model-clock.md)):
- **`GEN_SEED`** — зерно ГПСЧ генератора (детерминизм случайных решений). Не
данные, а число.
- **статический сид** (`data/*.jsonl`) — учебный демо-датасет режима `bootstrap`
(уроки 06) и **калибровочный эталон** генератора (профиль ниже — опорные
цифры). По мере появления «стартовой истории стенда» его учебная роль смещается
на **dev-фикстуру и эталон** (быстрый повторяемый вход для пайплайна STG→DM, не
зависящий от генератора); смещение зафиксировано как будущее направление в
ADR-0005, на время разработки сид и поток сосуществуют.
- **стартовая история стенда** — сгенерированное прошлое (заливка `K → ∞` +
заморозка состояния), с которого живой стенд стартует непрерывно. Ещё не
строим; см. ADR-0005.
### Модельное время и масштаб (×K)
**Модельное время стенда** отвязано от настенных часов: генератор крутит
внутренние часы, а драйвер задаёт скорость — ×1 (как реальное время), ×K
(ускоренно, учебная «ручка» урока 7) или `K → ∞` (мгновенная заливка прошлого).
При ускорении «сейчас» стенда уходит вперёд настенного времени — это свойство, не
баг (ключ аналитики — `event_timestamp`). Решение и режимы —
[ADR-0005](./docs/adr/0005-generator-model-clock.md).
## Почему на демо `Unique Users == Unique Sessions`
В демо-датасете (`data/*.jsonl`) каждый пользователь имеет **ровно один**
`click_id` — связь user↔визит строго 1:1 (проверено: 99 пользователей =
99 `click_id` в полном файле). Поэтому `uniqExact(user_domain_id)` и
`uniqExact(click_id)` дают одинаковое число.
Это **свойство конкретного датасета, а не закон модели и не баг расчёта.** В
реальных данных пользователь возвращается несколькими визитами, и числа
расходятся. Поэтому на дашборде мы **не** показываем две одинаковые KPI-плитки
(это читалось бы как ошибка) — вместо дубля идут производные метрики
(события на визит, доля визитов с одним событием), а различие «пользователь vs
сессия» объясняется текстом урока.
Синтетический генератор (ветка `feature/data-generator`) проблему не решает, а
усугубляет: он штампует свежий `click_id` на каждое событие, и `click_id`
вырождается в «событие». См. `generator/KNOWN_ISSUES.md` на той ветке.
## Профиль сид-датасета (измерено 2026-06-10, полные файлы)
Опорные цифры о статическом сиде (`data/*.jsonl`) — для калибровки генератора
и проверки гипотез. Измерено по **полным** файлам (1000 событий); внимание:
ранние оценки «1..7 событий на визит» делались по малому срезу и неверны.
- **Объём:** 1000 событий, 99 визитов (`click_id`), 99 пользователей (1:1).
- **Длина визита:** 1..27 событий, медиана 10, среднее 10.1.
- **Паузы между событиями визита:** медиана ~20 с, p95 ≈ 17 мин,
максимум ≈ 40 мин; пауз длиннее 30 минут — 11 штук. Разброс времени внутри
одного `click_id` — до 49 минут.
- **Воронка:** 25% визитов достигают `/confirmation`. Пути — с петлями:
возвраты на `/home`, просмотр обоих товаров; у 21 из 25 «купивших» визитов
есть события **после** `/confirmation`, у 9 — `/confirmation` встречается
дважды. То есть `/confirmation` — последний *шаг воронки*, но не обязательно
последнее *событие визита*.
- **Страницы (по событиям):** `/home` 426, `/product_a` 236, `/product_b` 157,
`/cart` 93, `/payment` 53, `/confirmation` 35. `event_type` — 100% `pageview`.
## Слои данных
`STG → ODS → DDS → DM` — см. [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md).
Витрины (`dm.*`) и дашборд Superset строятся на статическом сиде, не на потоке.