feat(stand): make up наполняет стенд стартовым миром, опись сторожит его

Зачем
Стенд поднимался пустым, и всякая приёмка следующих этапов начиналась с
ручной заливки данных. Теперь `make up` сам приводит стенд к одному и тому
же состоянию, а в git лежит то, чем это состояние проверяется.

Что
- Опись мира `data/world-inventory.json`: паспорт (зерно, версия
  генератора, хеш каталога) и по строке на каждый из восьми дней — дата,
  число событий, хеш байтов. Собирается `make inventory`, свежесть сторожит
  `test_inventory.py` — тем же способом, что свежесть описания выгрузки.
- Разовая служба `world-init` вышла из-под профиля и играет в топик восемь
  дней при каждом подъёме; зависимый у неё — `airflow-init`, иначе `--wait`
  считает успешно отработавшую службу упавшей.
- `scripts/wait-for-world.sh` — вторая половина `make up`: приём
  асинхронный, поэтому ждать надо доезда до `ods.event`, а не завершения
  заливки. Ограниченный цикл опроса, не пауза наугад.
- Девятая проверка `make check-clickhouse`: подневный счёт событий против
  описи, рамка по датам стартового мира, счёт через `FINAL`. При
  расхождении называет, где искать, — в событиях или в браке.
- Порог «день ≤ 30 с» снят из спеки генератора в обоих местах: замер дал
  1,7 с, порог был выше факта в восемнадцать раз. На его месте — замеры с
  датой. Раздел 9 спеки закрыт: открытых вопросов не осталось.
- Слова: «манифест» стал описью мира, «зерновой мир» — стартовым миром
  (решение владельца). Оба заведены в словарь CONTEXT.md.

Проверка
`make clean && make up` с нуля — 2 м 50 с, доехало ровно 401 185 событий.
`make check-clickhouse` зелёный (8 с), `make smoke` зелёный (9 с),
`make test` — 407 тестов за 71 с, `make lint`, `make typecheck`,
`make config-test` зелёные.

Что проверка умеет краснеть, снято двумя поломками: снос партиции
2026-06-03 дал диагноз «не доехали до ODS», негодная строка в сырье —
«сломан разбор». Строки опыта убраны, день переигран, счёт вернулся.
Тест свежести проверен молчаливой правкой цены в каталоге: покраснел.

Ссылка: #42
This commit is contained in:
ddadmin committed 2026-08-07 18:37:01 +03:00
1 parent 30a1e9e567
commit 7c9eeedc40
20 files changed
+625 -132

No files matched your search

+14 -14
View File
@@ -253,7 +253,7 @@ CSV в репозитории (`data/catalog/products.csv`: `sku`, `name`, `cate
Пятое — **опоздание** — бесплатно даёт формат доставки: часть заказов
впервые появляется в слепке D+1/D+2 («вчера не сходилось, сегодня сошлось»),
ориентир ~10%. Точные доли фиксируются при пересборке эталонного мира;
манифест хранит точные счётчики по каждому классу расхождений (отмены,
опись хранит точные счётчики по каждому классу расхождений (отмены,
потери, дубли).
Не берём: сироту-фрод (`purchase` есть, а заказа не будет никогда) —
@@ -286,7 +286,7 @@ CSV в репозитории (`data/catalog/products.csv`: `sku`, `name`, `cate
`uniq(посетителей) > uniq(людей)`, менти выводит расхождение сам.
Константа мира: каждый двухкуковый покупатель делает минимум по одному
заказу с каждой куки — иначе вторая кука не попадает в карту соответствий
(она строится только из покупок) и лаба не воспроизводится. Манифест
(она строится только из покупок) и лаба не воспроизводится. Опись
хранит число именно таких пар.
- Витрины разводят имена честно: **«посетители»** (`uniq(ClientID)`) и
**«известные пользователи»** (после склейки) — оба числа рядом в дашборде.
@@ -464,19 +464,19 @@ Kafka день переигрывается генератором заново:
пачками), Postgres остаётся только служебной базой Airflow. Смешанность
ландшафта выражена режимами и частотами, а не второй трубой.
## 8. Эталонный мир и манифест
## 8. Эталонный мир и опись
В git хранится только манифест эталонного мира; сам снимок (14 модельных
В git хранится только опись эталонного мира; сам снимок (14 модельных
дней) генерируется на месте — при `make up` и при проверках (решение
развилки «Производительность» карты #26, подробности —
[спека генератора](2026-08-01-generator.md), раздел 5). Манифест несёт
[спека генератора](2026-08-01-generator.md), раздел 5). Опись несёт
паспорт мира (каноническое зерно, версия генератора), контрольные счётчики
и хеши по дням; проверки «пустой git diff» и «пересгенерируй день N —
сравни хеш» живут на нём. Политика версионирования артефакта (бывший туман
карты #10) закрыта этим же ходом: версионируется манифест.
Контрольные числа манифеста:
сравни хеш» живут на ней. Политика версионирования артефакта (бывший туман
карты #10) закрыта этим же ходом: версионируется опись.
Контрольные числа описи:
- заказная сторона: заказы и выручка по дням; манифест хранит точные
- заказная сторона: заказы и выручка по дням; опись хранит точные
счётчики по каждому классу расхождений (отмены, потери, дубли, дельты сумм) —
самопроверка лабы сверки;
- идентичность: uniq кук, uniq известных пользователей, число двухкуковых
@@ -497,7 +497,7 @@ v2 стартует пустым, поэтому объём ниже — это
| SQL | DDL по слоям и ролям (ON CLUSTER, Replicated*, Distributed; раскладка файлов — в доке хранилища) + трансформации событий, заказов, identity, сверки + словарь | L — ~12–15 файлов, главная сложность |
| Airflow | DAG'и по образцу v1: etl_pipeline (партиционная переобработка, ожидание дневного батча заказов — сенсор/Datasets), world_init/next_day, helpers | M — ~5–6 файлов |
| Superset | датасеты + дашборд с тремя новыми сюжетами | M — 2 файла |
| Эталонный мир | пересборка снимка на месте, манифест-счётчики, чек-скрипты | M–L |
| Эталонный мир | пересборка снимка на месте, счётчики описи, чек-скрипты | M–L |
| Мониторинг | дашборды Grafana «данные», «кластер», «запросы»; ClickHouse источником данных, панели на SQL; Prometheus тонким полом (ADR 0002) | M — конфиги и дашборды |
| Документация | доки v2 пишутся заново (см. раздел 12) | M, в тех же PR |
@@ -534,15 +534,15 @@ v2 стартует пустым, поэтому объём ниже — это
2. DDL и генератор (слиты в один этап — DDL проверяется только настоящими
данными): базы и таблицы событий ON CLUSTER, приём `hits` обеими нодами;
широкое событие, таксономия, анонимность, N:1 (клиентская сторона
целиком). В конце этапа фиксируется маленький «зерновой» мир для
целиком). В конце этапа фиксируется маленький стартовый мир для
стабильных приёмок следующих этапов (полная пересборка эталонного мира —
отдельный этап 7).
3. Заказы и каталог: генератор слепков, STG/ODS/DDS заказа, словарь.
4. Трансформации и витрины: сессии, identity_map, выручка, сверка A+C.
5. Airflow: `etl_pipeline` (партиционная переобработка, ожидание дневного
батча заказов — сенсор/Datasets).
6. Расхождения B+D и опоздания; счётчики манифеста.
7. Эталонный мир: манифест и пересборка снимка, чек-скрипты; CI-генерация
6. Расхождения B+D и опоздания; счётчики описи.
7. Эталонный мир: опись и пересборка снимка, чек-скрипты; CI-генерация
на amd64 и arm64.
8. Superset-дашборд v2.
9. Мониторинг и runbook «keeper упал / DDL повис в очереди». Состав дашбордов
@@ -563,7 +563,7 @@ v2 стартует пустым, поэтому объём ниже — это
- Лабы и курс: v2 — другой стенд, лабы для него пишутся с нуля отдельной
работой после этой спеки; редизайн лаб v1 (#7) остаётся в v1 и сюда не
переносится. Спека даёт будущим лабам только опорные точки — контрольные
числа манифеста (сверка, идентичность). Явное следствие: после этапа 9
числа описи (сверка, идентичность). Явное следствие: после этапа 9
стенд работает, но учебного пути на нём ещё нет.
- Инкрементальный ETL (#8) — свой issue.
- Реплики (2×2), HAProxy, репликационная эксплуатация — в лекцию, не в стенд.