docs(specs): правки по холодному ревью — долг словаря и потерянные доводы
- Зачем:
- холодное ревью (Fable, свежая сессия) нашло невыполненный хвост
тикета #32 и места, где доводы резолюций сжались до непонятности.
- Что:
- мастер-спека 1.1: «склад» заменён на «хранилище» (хвост #32);
CONTEXT.md: DWH в избегаемых, отдельная статья «Пакетный режим».
- спека: восстановлены доводы «на маке и в WSL тоже» и «менти упрётся
в красный чек манифеста»; обещания про diff привязаны к манифесту;
темп ×60 и расчёт порога согласованы с числами разделов; заголовок
притока честен про затухание; выход за мандат оговорён в «Зачем».
- раздел 9: добавлены числа притока и календарная дата-константа D0;
заметка исследования: у Faker единицы «значений/с», не «строк/с».
- Проверка:
- вычитка; решения развилок не пересматриваются, правки текстовые.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+5
-1
@@ -9,7 +9,7 @@
|
||||
Аналитическая база стенда — кластер ClickHouse со слоями STG/ODS/DDS/DM.
|
||||
Принимающая сторона границы «трекер | хранилище»: нормализует имена и стили
|
||||
источников, строится по их документации.
|
||||
_Избегать_: склад, склад данных
|
||||
_Избегать_: склад, склад данных, DWH
|
||||
|
||||
**Состав мира**:
|
||||
Постоянная часть мира генератора — популяция посетителей с календарём их
|
||||
@@ -30,6 +30,10 @@ _Избегать_: состояние мира
|
||||
Проигрывание текущего модельного дня в реальном времени с ускорением;
|
||||
включается по требованию, не постоянный фон.
|
||||
|
||||
**Пакетный режим**:
|
||||
Проигрывание готового дня пачкой, без темпа: заливка снимка при старте
|
||||
стенда, пересборки и проверки.
|
||||
|
||||
**Граница суток**:
|
||||
Единственный структурный шов модели: сессии режутся по ней, слепок заказов
|
||||
снимается на ней, день проживается только целиком.
|
||||
|
||||
@@ -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 |
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ JSON-поле `ecommerce`. Сессий в потоке нет — их мент
|
||||
- **Имена колонок — как в облачной выгрузке Метрики** (`ClientID`,
|
||||
`UTCEventTime`, `purchaseID`…). Сырой слой хранит имена источника; свои
|
||||
snake_case-имена появляются в DDS/DM. Это учебный пункт: у каждого
|
||||
источника — свой стиль, нормализует его склад, а не трекер.
|
||||
источника — свой стиль, нормализует его хранилище, а не трекер.
|
||||
- **Идентификаторы — числовые UInt64** (`WatchID`, `VisitID`, `ClientID`),
|
||||
UUID уходят. Исследование советовало UUID не трогать, но тот совет исходил
|
||||
из цены переделки текущего генератора; v2 пишет генератор заново, цена
|
||||
|
||||
@@ -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 мастер-спеки):
|
||||
с манифестным решением напрашивается мини-манифест зернового мира — форму
|
||||
|
||||
Reference in New Issue
Block a user