Files
clickstream-ch-kafka-supers…/docs/specs/2026-06-14-generator-model-time-and-startup-history.md
T
ddadminandClaude Opus 4.8 fa93aef150 docs(context): глоссарий и спеки приведены в соответствие с ADR-0006
- Зачем:
  - ADR-0006 сделал генерацию единственным источником аналитики, а статический
    сид — архивным; глоссарий и спеки это ещё не отражали.
- Что:
  - CONTEXT.md: «статический сид» переименован в «архивный статический сид»
    (короткое имя сохранено), описан как временная кладовка значений с целью
    полного вывода; «стартовая история» получила синонимы «стартовый сид» и
    «новый сид»; раздел «Слои данных» отмечает переход аналитики на генерацию.
  - мат-спека: разделы «Персистентность через рестарты» и «Воспроизводимость»
    помечены как переописанные в спеке модельного времени (ссылкой, без повтора).
  - спека модельного времени: синоним «стартовый сид» добавлен в определение и
    в заметку о влиянии на документацию.
- Проверка:
  - git diff: термины и перекрёстные ссылки читаются непротиворечиво; ADR не
    правились (статус «архивный» не переносится в документы до ADR-0006).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 15:31:11 +03:00

200 lines
17 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.
# Модельное время на практике: сохранение состояния, заливка прошлого и стартовая история
Дата: 2026-06-14
Статус: Draft
Связано: [ADR-0005](../adr/0005-generator-model-clock.md) (решение про модельные
часы — эта спека его дорабатывает), [ADR-0006](../adr/0006-generation-as-sole-analytics-source.md)
(стартовая история как источник аналитики), мат-спека
[`2026-06-10-generator-math-model.md`](./2026-06-10-generator-math-model.md)
(её разделы «Персистентность через рестарты» и «Воспроизводимость» здесь
**приводятся в соответствие** с модельным временем — не повторяются, а
переописываются ссылкой), [`CONTEXT.md`](../../CONTEXT.md), задача
[`06-state-v2-and-restart`](../../.scratch/feature-data-generator/issues/06-state-v2-and-restart.md)
(сохранение состояния построено на настенных часах).
Уровень документа — **модель и правила**, как в мат-спеке. Код, имена настроек,
формат конфигурации и сам способ управления ходом часов — за исполнителем, в
рамках правил этой спеки (как условились в ADR-0005).
## Проблема
ADR-0005 решил отвязать время генератора от реальных часов и ввёл три скорости
его хода: обычную (×1), ускоренную (×K) и мгновенную «промотку» прошлого. Но два
правила старой модели — как генератор сохраняет состояние между перезапусками и
как добивается повторяемости — описаны от реальных часов. А ADR-0006 сделал
стартовую историю (готовое сгенерированное прошлое) единственным источником
данных стенда.
Осталось довести модель до конца: как работает промотка прошлого, его заморозка и
непрерывный запуск стенда с этого момента; как при этом меняются правила
сохранения состояния и повторяемости; и как всё это проверить. Всё это — **один
механизм**: промотать прошлое → заморозить → продолжить живьём. Это один и тот же
путь сохранения и восстановления, просто с разных сторон.
## Цели
- **Дать генератору собственные часы и сделать поток повторяемым.** Генератор
должен отсчитывать время от своей точки отсчёта (ниже — «стартовая модельная
точка `T0`»), а не от реальных часов компьютера. Тогда при одних и тех же
настройках он каждый раз порождает один и тот же поток — это нужно для тестов и
для повторяемых уроков.
- **Переписать правила сохранения и восстановления состояния по этим часам.**
Сейчас они завязаны на реальное время. Главное правило — «если посетитель молчал
дольше 30 минут, считаем, что он ушёл» — должно мерить эти 30 минут по часам
генератора. И поток больше не должен зависеть от того, в котором часу реального
дня запущен генератор.
- **Научиться быстро «проматывать» прошлое и замораживать его как стартовый набор
данных.** Генератор прокручивает время без пауз от точки отсчёта до нужного
момента, порождает события прошлого и сохраняет «слепок» своего состояния. Этот
замороженный набор — то, с чего свежий стенд начинает жить, уже имея историю:
здоровую пирамиду «пользователей меньше, чем визитов, а визитов меньше, чем
событий» с первой минуты.
- **Описать, как всё это проверить — в два шага.** Сначала числами: агент
поднимает стенд и сверяет данные в ClickHouse. Потом глазами: человек смотрит на
дашборды и видит, что распределение похоже на задуманное, а стенд «дышит» во
времени.
## Чего здесь не делаем
- **Не учим генератор придумывать «фактуру» сам** (браузеры, гео, устройства,
метки кампаний). Это отдельная спека — следствие ADR-0006. Пока её нет,
стартовая история берёт фактуру из статического сида; это осознанно временно.
- **Не описываем здесь перестройку процесса** на новый сид (загрузка
`kafka_load_dag`, витрины/Superset, уроки) — это решено в ADR-0006 и
проектируется отдельно. Эта спека — только про сам генератор.
- **Не фиксируем** имена настроек, формат конфигурации и сам алгоритм — это за
исполнителем.
- **Не добавляем** новые типы событий и инкрементальную загрузку ETL.
## Модель
### Точка отсчёта и скорость хода часов
У генератора своя точка отсчёта времени — **стартовая модельная точка `T0`**
(задаётся в настройках). В метку события (`event_timestamp`) пишется это
внутреннее время. Скорость, с которой оно идёт относительно реальных часов,
переключается (в ADR-0005 — «драйвер часов»):
- **×1** — как реальное время;
- **×K** — в `K` раз быстрее;
- **заливка прошлого** — время гонится без пауз от `T0` до нужного момента
(особый случай очень большого `K`).
### Повторяемость
При одном и том же зерне `GEN_SEED`, одной и той же `T0` и одной скорости
генератор каждый раз даёт **один и тот же поток**. Это убирает прежнюю оговорку
мат-спеки «запуск в другой час даёт другой поток»: дневной коэффициент
(день/ночь) теперь считается по внутреннему времени от `T0`, а не по реальным
часам. Так раздел «Воспроизводимость» мат-спеки приводится в соответствие с
модельным временем.
### Заливка прошлого и стартовая история
«Промотать» прошлое — значит прогнать время без пауз от `T0` до момента `T_end` и
сгенерировать события этого отрезка. На выходе — события за `[T0, T_end]` и
**слепок состояния** генератора на момент `T_end`.
События плюс слепок и есть **стартовая история** («стартовый сид» или «новый сид» —
синонимы, новое значение слова «сид» не заводим). При создании стенда это прошлое заливается, и
генератор готов продолжить ровно с `T_end`. Стенд сразу живёт с готовой историей —
с настоящей пирамидой «пользователей меньше, чем визитов, визитов меньше, чем
событий», без вырождения статического сида, где пользователей ровно столько же,
сколько визитов.
### Сохранение и восстановление состояния
Правило «молчал дольше 30 минут — посетитель ушёл» остаётся; меняется только **от
какого момента отсчитывать эти 30 минут**:
- **после сбоя** — от реального «сейчас» (время и правда прошло) → как сегодня;
- **при запуске со стартовой истории** — от метки слепка (`T_end`) → разрыва
почти нет, активные визиты не обрываются, мир продолжается без шва.
Паузы между визитами одного пользователя и обязательная пауза перед возвратом
тоже считаются по внутренним меткам времени, а не по числу тактов. Если
сохранённое состояние повреждено или его нет — генератор стартует с чистого листа
(свежая популяция от `GEN_SEED`) и пишет предупреждение в лог. Так раздел
«Персистентность» мат-спеки приводится в соответствие с модельным временем.
### С какой скоростью стенд живёт дальше
После старта стенд продолжает жить на выбранной скорости. Скорость — это **ручка
под задачу**: чтобы увидеть медленные вещи (возвраты, сдвиг воронки после правки
таблицы переходов, «дыхание» суточной нагрузки) за учебное время, нужно ускорение
(×K) — иначе суточная волна разворачивается реальные сутки. Какой скорость будет
по умолчанию (×1 «как настоящий сайт» или ускоренная «учебная») — решает
урок/исполнитель; спека лишь фиксирует, что это ручка и что именно на ней держится
наблюдаемость медленных явлений.
### Временная опора на сид (фактура)
Пока генератор не умеет придумывать фактуру сам (ADR-0006), стартовая история
одевает события в данные из статического сида (браузер, гео, устройство, метки
кампаний). Это временно и не мешает: механизм времени и стартовой истории не ждёт
синтеза фактуры, а сид до его появления остаётся кладовкой готовых значений — но
уже не источником аналитики.
## Проверка
Два шага — это разделение труда: первый объективный и повторяемый (его делает
агент или CI), второй — человеческий взгляд.
### Шаг 1 — агент сверяет числа в ClickHouse
На чистом стенде с фиксированными `GEN_SEED` и `T0` залить стартовую историю,
прогнать STG→DM и проверить:
- **пирамида:** уникальных пользователей меньше, чем визитов, а визитов меньше,
чем событий;
- **воронка** убывает по шагам `/home → товары → /cart → /payment →
/confirmation`; доля дошедших до `/confirmation` — в нужном коридоре;
- **возвраты:** у части пользователей больше одного визита;
- **длина визита** и доля коротких визитов — в коридорах мат-спеки.
Главное: проверки **повторяемы** (тот же `GEN_SEED` и `T0` → те же числа в
пределах допуска) и записаны как команды и запросы, чтобы их мог прогнать агент
или CI без человека. Сами запросы и числовые коридоры — при реализации.
### Шаг 2 — человек смотрит на дашборды
Глазами убедиться, что:
- **распределение похоже на задуманное** — на наши спроектированные распределения
(мат-спека), а не на профиль чужого сида;
- **стенд «дышит»** — видна суточная волна нагрузки, копятся возвраты и воронка
(заметно на ×K).
Здесь всплывает нехватка: дашборда с распределением сгенерированных данных пока
нет. Superset построен на сиде, а Grafana показывает только скорость работы
сервиса, не форму данных. Закрыть можно двумя способами: переключить Superset на
генерацию (часть большой миграции — позже) или добавить простую панель
«распределение из ClickHouse» в Grafana. Пошаговые действия шага 2 — в будущий
runbook «проверка генератора на стенде».
## Решения и отклонённые варианты
- **Приняли:** одна спека на время, сохранение состояния и стартовую историю —
это один механизм, а не три задачи.
- **Приняли:** своя точка отсчёта `T0` как опора повторяемости; дневной
коэффициент считается по внутреннему времени.
- **Приняли:** проверка в два шага (агент — числа, человек — глаза).
- **Отклонили:** жёстко подгонять генерацию под профиль сида (см. ADR-0006).
- **Отклонили:** приводить сохранение состояния в соответствие с модельным
временем отдельно от стартовой истории — это разрезало бы один механизм надвое.
## Влияние на документацию
- **Мат-спека:** разделы «Персистентность через рестарты» и «Воспроизводимость»
пометить как переописанные в модельном времени этой спекой (ссылкой, без
повтора).
- **ADR-0005:** направление «стартовая история» — отмечено как запущенное.
- **CONTEXT.md:** «стартовый сид» / «новый сид» = «стартовая история стенда»
(синонимы, не новое значение); роль статического сида — по ADR-0006.
- **Будущий runbook** «проверка генератора на стенде» — шаги шага 2.
- **`generator/README.md`, `docs/OPERATIONS.md`** — при реализации: скорость хода
часов, ×K, стартовая история, проверки шага 1.