Files
clickstream-ch-kafka-supers…/docs/adr/0005-generator-model-clock.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

9.4 KiB
Raw Blame History

ADR-0005: Модельные часы генератора, отвязанные от настенного времени

Принято: 2026-06-11 Статус: accepted Связано: ADR-0004 (расширяет — steady-stream как синтетический генератор), CONTEXT.md (термины времени и «сида»), мат-спека docs/specs/2026-06-10-generator-math-model.md (разделы «Персистентность» и «Воспроизводимость» написаны от настенных часов — будут реконсилированы отдельным заходом), задача 06-state-v2-and-restart (state v2 и правило рестарта построены на настенных часах).

Решение

Модельное время генератора расцеплено от настенных часов (now()). Генератор потребляет метку такта извне; «драйвер часов» задаёт отображение модельного времени на реальное. Шов под это уже существует в коде (такт принимает метку времени параметром).

Три режима — это драйверы поверх одного шва, а не отдельные архитектуры:

  • живой ×1 — модельное время идёт со скоростью настенных часов (текущее поведение);
  • живой ускоренный ×K — модельное время идёт в K раз быстрее реального (K — конфигурация, дефолт ×1);
  • заливка ×∞ — модельное время гонится без сна от стартовой точки до целевой; частный случай K → ∞.

Живой стенд масштабируется ×K, дефолт ×1. Ускорение — учебная «ручка» урока 7: разогнать стенд и вживую увидеть медленные явления (возвраты, межсессионные паузы, сдвиг воронки после правки марковской таблицы переходов).

Метки событий пишутся по модельному времени. При ускорении/заливке «сейчас» стенда расходится с настенными часами — для аналитического стенда (ключ — event_timestamp, внеочередная вставка в ClickHouse нормальна) это безвредно; фиксируем как явное свойство, чтобы «время стенда ≠ твои настенные часы» не читалось как баг.

Имена параметров, формат конфигурации и алгоритм драйвера — за исполнителем; здесь не фиксируются.

Контекст

Текущая модель неявно держит инвариант «время генератора ≡ реальное время ×1»: главный цикл спит до настенного такта, метки событий = now() + смещения, а паузы внутри визита «проживаются» в реальном времени. Для учебного плана это неудобно по двум причинам:

  • Медленные явления не успеть показать. Возвраты (~2 ч межсессионной паузы), накопление воронки, удержание разворачиваются часами и днями — в рамках урока на ×1 их не увидеть.
  • Историческую глубину живой генератор не создаёт вовсе. За реальную минуту рождается ~λ событий реального времени; глубину сейчас даёт только статический сид.

Дополнительно: воспроизводимость на ×1 завязана на настенный час (дневной коэффициент, метки) — запуск в другой час даёт другой поток. Расцепление с фиксированной стартовой модельной точкой эту зависимость снимает.

Связь с задачей 06: персистентность (state v2 + рестарт) уже построена на настенных часах. Расцепление меняет смысл «правила 30 минут» — см. «Последствия». Эта ADR — отдельный слой поверх 06; она не правит то, что задачи 06/07 уже зафиксировали (их доводила автономная петля субагентов на реал-тайм-модели), реконсиляция идёт отдельным заходом.

Рассмотренные варианты

  • Оставить ×1 (статус-кво). Отклонено: не решает учебное неудобство; историческую глубину даёт только сид, медленные явления вживую не показать.
  • Живой всегда ×1, «быстро» только как разовая заливка прошлого. Жизнеспособно, но слабее: урок 7 не может вживую ускорить наблюдение, и это два разных механизма (живой стенд и батч-заливка) вместо одного.
  • Живой масштабируется ×K, дефолт ×1; заливка = K → ∞ (принято). Один механизм поглощает все режимы; ускорение прямо служит уроку 7; дефолт ×1 сохраняет «дыхание» настоящего сайта; пре-заливка прошлого тоже доступна (бакаешь на огромном K, живёшь на умеренном). Цена — метки уходят вперёд настенных часов при разгоне (безвредно для аналитики).

Последствия

  • Режимы живой / ускоренный / заливка — конфигурация одного драйвера часов; реализация и имена параметров — за исполнителем.

  • Правило 30 минут (задача 06) переопределяется в модельном времени. Формула не меняется: разрыв между модельной меткой следующего события визита и модельным временем возобновления больше 30 минут → визит закрывается; меньше → продолжается. Меняется origin возобновления — он становится параметром:

    • восстановление после краха — origin = настенное now() (время реально прошло) → поведение как сегодня;
    • продолжение из заморозки/сида — origin = метка снимка → разрыв ≈ 0, мир продолжается непрерывно, активные визиты не закрываются.

    Текущий код 06 берёт now() жёстко: корректно для краха, требует параметризации для продолжения.

  • Будущее направление (ещё не строим): «стартовая история стенда». Заливка K → ∞ + заморозка state v2 даёт сгенерированное прошлое, с которого живой стенд стартует непрерывно (те же люди возвращаются — users < sessions рождается вживую, без костыля статического сида users == sessions). При этом роль статического сида со временем смещается с «учебный датасет» на «dev-фикстура и калибровочный эталон» (термины — в CONTEXT.md). На время разработки сид и поток сосуществуют (как зафиксировано в ADR-0004); продолжение-из-сида — отдельная будущая задача после готовности генератора.

  • Воспроизводимость улучшается при фиксированной стартовой модельной точке; реконсиляция разделов «Персистентность» и «Воспроизводимость» мат-спеки — отдельный заход после ревью эксперимента с автономной петлёй.