diff --git a/CONTEXT.md b/CONTEXT.md index de3c1d0..31f440d 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -9,7 +9,7 @@ Аналитическая база стенда — кластер ClickHouse со слоями STG/ODS/DDS/DM. Принимающая сторона границы «трекер | хранилище»: нормализует имена и стили источников, строится по их документации. -_Избегать_: склад, склад данных +_Избегать_: склад, склад данных, DWH **Состав мира**: Постоянная часть мира генератора — популяция посетителей с календарём их @@ -30,6 +30,10 @@ _Избегать_: состояние мира Проигрывание текущего модельного дня в реальном времени с ускорением; включается по требованию, не постоянный фон. +**Пакетный режим**: +Проигрывание готового дня пачкой, без темпа: заливка снимка при старте +стенда, пересборки и проверки. + **Граница суток**: Единственный структурный шов модели: сессии режутся по ней, слепок заказов снимается на ней, день проживается только целиком. diff --git a/docs/research/2026-08-01-python-batch-generation-speed.md b/docs/research/2026-08-01-python-batch-generation-speed.md index f771856..db9ac88 100644 --- a/docs/research/2026-08-01-python-batch-generation-speed.md +++ b/docs/research/2026-08-01-python-batch-generation-speed.md @@ -19,7 +19,7 @@ | Случайные значения numpy (векторно) | ~10^8 значений/с | таблица numpy | | Векторная сборка колонок события | ~10^6–10^7 строк/с | локальная проверка | | Посточная генерация (random + dict) | ~10^4–10^5 строк/с | локальная проверка | -| Посточная генерация через Faker | ~10^3–10^4 строк/с | бенчмарк mimesis | +| Посточная генерация через Faker | ~10^3–10^4 значений/с | бенчмарк mimesis | | Сериализация orjson (запись ~50 КБ) | ~10^5 док/с | README orjson | | Сериализация stdlib json (та же запись) | ~10^4 док/с | README orjson | diff --git a/docs/specs/2026-07-30-stand-v2-realism.md b/docs/specs/2026-07-30-stand-v2-realism.md index e1ce3f7..32c2d87 100644 --- a/docs/specs/2026-07-30-stand-v2-realism.md +++ b/docs/specs/2026-07-30-stand-v2-realism.md @@ -56,7 +56,7 @@ JSON-поле `ecommerce`. Сессий в потоке нет — их мент - **Имена колонок — как в облачной выгрузке Метрики** (`ClientID`, `UTCEventTime`, `purchaseID`…). Сырой слой хранит имена источника; свои snake_case-имена появляются в DDS/DM. Это учебный пункт: у каждого - источника — свой стиль, нормализует его склад, а не трекер. + источника — свой стиль, нормализует его хранилище, а не трекер. - **Идентификаторы — числовые UInt64** (`WatchID`, `VisitID`, `ClientID`), UUID уходят. Исследование советовало UUID не трогать, но тот совет исходил из цены переделки текущего генератора; v2 пишет генератор заново, цена diff --git a/docs/specs/2026-08-01-generator.md b/docs/specs/2026-08-01-generator.md index 537c4b4..870442d 100644 --- a/docs/specs/2026-08-01-generator.md +++ b/docs/specs/2026-08-01-generator.md @@ -20,7 +20,10 @@ Здесь не переоткрывается решённое мастер-спекой: модель данных события, таксономия, анонимность, механика заказов (границы мандата #14). Генератор -слепков заказов — этап 3: эта спека лишь не должна ему мешать. +слепков заказов — этап 3: эта спека лишь не должна ему мешать. Один +осознанный выход за границы: решение о хранении снимка (раздел 5) формально +касается этапа 7 — расширение подтверждено владельцем в резолюции +«Производительности». ## Целевая картина одним взглядом @@ -48,15 +51,18 @@ плана состава, а не копятся в состоянии. Глобальные инварианты («каждый двухкуковый покупатель заказал с обеих кук») гарантируются планом — счётчики манифеста известны до генерации событий. -- **Состав не замкнут: посетители появляются и уходят.** План состава задаёт - календарь появления — у каждого посетителя есть дата первого визита и - профиль возвратов, заметная доля кук одноразовая, как в живом трафике. - Новые посетители появляются на всём протяжении оси: uniq(ClientID) растёт - с горизонтом, DAU и MAU не сходятся в одно число. Приток — часть плана, а - не мутация: счётчики манифеста по-прежнему известны до генерации - (уточнение по вычитке владельца, 2026-08-01). +- **Состав не замкнут: посетители появляются и затухают.** План состава + задаёт календарь появления — у каждого посетителя есть дата первого + визита и профиль возвратов, включая затухание: заметная доля кук + одноразовая, как в живом трафике. Новые посетители появляются на всём + протяжении оси: uniq(ClientID) растёт с горизонтом, дневная и накопленная + аудитории не сходятся в одно число. Приток — часть плана, а не мутация: + счётчики манифеста по-прежнему известны до генерации (уточнение по + вычитке владельца, 2026-08-01); его числа — раздел 9. - **Своя ось модельного времени.** Мир рождается в фиксированный день D0 (понедельник — см. раздел 5); реальный календарь в модели не участвует. + В `EventDate`/`UTCEventTime` дни оси ложатся конкретными датами, но это + константа мира, от даты запуска не зависящая (значение — раздел 9). Между прогонами живут только зерно и позиция на оси: выключенный ноутбук — мир замер, потом продолжил. - **Два режима движения по одной оси.** Пошаговый — базовый для лаб: старт @@ -89,13 +95,14 @@ - **Обещание — содержимое до байта.** Два прогона с одним зерном дают тот же набор событий: те же `WatchID`/`VisitID`, поля, метки модельного времени. - Артефакт снимка при пересборке побайтово совпадает: канонический порядок - ключей и строк, пустой diff означает «ничего не изменилось». Вне + Снимок при пересборке побайтово совпадает: канонический порядок ключей и + строк; сверка — по хешам манифеста, а два локально пересобранных снимка + сравнимы обычным diff — пустой означает «ничего не изменилось». Вне обещания — транспорт: офсеты и партиции Kafka, какая нода прочитала, `_ingested_at`, темп живого дня. - **Условия обещания.** Детерминизм держится при зафиксированном `uv.lock` - и внутри канонического контейнера (везде Linux; переменная — только - архитектура CPU). Истина — CI на Linux; сходимость любой машины проверяет + и внутри канонического контейнера — то есть везде Linux, на маке и в WSL + тоже; единственная переменная — архитектура CPU. Истина — CI на Linux; сходимость любой машины проверяет скрипт «пересгенерируй день N — сравни хеш с манифестом». Расхождение на любой платформе — баг генератора, а не допуск. - **Раздача зерна — иерархией подпотоков.** Корневое зерно → состав мира; @@ -103,7 +110,8 @@ торговые события, расхождения, опоздания — в фиксированном порядке. По построению: параллельный прогон равен последовательному; продление истории днём N+1 не трогает дни 1…N; правка одного компонента меняет - только его часть снимка — diff читаем. + только его часть снимка — в манифесте меняются хеши только затронутых + дней, дифф двух локальных пересборок читаем. - **Механизм подпотоков — `numpy.random.SeedSequence`.** Сверено через Context7 по документации numpy (2026-08-01): `spawn(n)` порождает детей расширением `spawn_key`, потомок полностью определяется парой @@ -226,10 +234,12 @@ хранят. Манифест несёт паспорт мира, счётчики и хеши по дням; проверки «пустой git diff» и «пересгенерируй день N — сравни хеш» живут на нём. Каждый `make up` — живая демонстрация детерминизма. Честная потеря — - страховка от платформенного бага; смягчение — CI гоняет генерацию на - amd64 и arm64. + страховка на случай платформенного бага: раньше менти с расходящимися + байтами мог взять готовый снимок из git, теперь он упрётся в красный чек + манифеста; смягчение — CI гоняет генерацию на amd64 и arm64. - **Живой день — ×60 по умолчанию**: модельные сутки за 24 реальные минуты, - суточная волна разворачивается на глазах, темп ~35–100 событий/с. Число — + суточная волна разворачивается на глазах; темп в среднем ~35 событий/с, + в пиковые часы сильных дней — до ~100. Число — значение по умолчанию, переопределяется флагом проигрывателя: ускорение — свойство транспорта, вне обещания воспроизводимости, константой мира не делается. @@ -249,7 +259,7 @@ | Лаг живого дня | секунды | **Автоматический порог один: полный день (50 тыс. событий) генерируется -≤ 30 с.** Расчёт по планке исследования (~2–5×10⁵ событий/с на ядро) — около +≤ 30 с.** Расчёт по планке исследования (~2–5×10⁵ событий/с на ядро) — доли секунды; порог держит машинный разброс ×2–5 и ловит деградацию на 1–2 порядка: Faker в горячем цикле, случайная квадратичность. Реализация — pytest-тест с маркером `perf` и таймаутом-обрубанием: обязателен в CI, @@ -327,6 +337,10 @@ pytest-тест с маркером `perf` и таймаутом-обрубан - интерфейс запуска генератора (CLI / цели make) и как он делит режимы проигрывателя; кто его зовёт в стенде — даги `world_init`/`next_day` из оценки мастер-спеки (раздел 9) — и в каком контейнере он живёт; +- числа притока посетителей: доля одноразовых кук и темп появления новых + (раздел 1); +- календарная дата-константа D0: каким числом дни оси ложатся в + `EventDate`/`UTCEventTime`; - формат описания мира и конфигурации (что константа кода, что параметр); - как фиксируется «зерновой» мир конца этапа 2 (раздел 9 мастер-спеки): с манифестным решением напрашивается мини-манифест зернового мира — форму