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:
@@ -13,7 +13,9 @@ Prometheus, Grafana и общая база Postgres для метаданных.
|
||||
кликстрима в [`generator/`](generator/) собран целиком со стороны клиента: у
|
||||
него есть контракт схемы события, из которого собрано
|
||||
[описание выгрузки](docs/formats/clickstream-event.md), модельный мир и
|
||||
проигрыватель, отправляющий дни в Kafka или в файл.
|
||||
проигрыватель, отправляющий дни в Kafka или в файл. События доезжают до
|
||||
типизированного `ods.event`, а `make up` наполняет стенд стартовым миром —
|
||||
первой неделей модельного времени.
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
@@ -26,10 +28,12 @@ Prometheus, Grafana и общая база Postgres для метаданных.
|
||||
хватает, стенд одинаково хорошо живёт на недорогом VPS.
|
||||
|
||||
Проверкам на поднятом стенде также нужны `curl`, `jq`, `awk`,
|
||||
`grep`, `sed`, `tail`, `sleep` и `timeout`. По умолчанию должны быть свободны
|
||||
`grep`, `sed`, `tail`, `sleep` и `timeout`. `jq` нужен и самому `make up`: им
|
||||
читается опись мира, по которой он ждёт заливки. По умолчанию должны быть свободны
|
||||
порты `23000`, `28080`, `28088`, `28123`, `28124`, `29000`, `29001`, `29090`
|
||||
и `29092`. Проверкам без стенда — `make config-test`, `make lint`,
|
||||
`make typecheck`, `make test` — и сборке документации `make docs` нужен `uv`.
|
||||
`make typecheck`, `make test` — и сборкам `make docs` и `make inventory` нужен
|
||||
`uv`.
|
||||
|
||||
Стенд запускается без `.env`:
|
||||
|
||||
@@ -70,6 +74,7 @@ make smoke
|
||||
`superset-init` завершаются с кодом 0. Первый обновляет схему Airflow,
|
||||
подготавливает администратора и подключение к `clickhouse-01`. Второй обновляет
|
||||
Superset, создаёт администратора и импортирует подключение к `clickhouse-02`.
|
||||
С нуля подъём занимает около трёх минут, на живом стенде — около минуты.
|
||||
|
||||
`make smoke` за секунды спрашивает, собран ли стенд: зависимости машины,
|
||||
здоровье контейнеров, устройство keeper, ответ Kafka с машины через отображённый
|
||||
@@ -105,7 +110,10 @@ Kafka по той же причине спрашивают снаружи. Её
|
||||
|
||||
`make check-clickhouse` запускает отдельную глубокую проверку ClickHouse:
|
||||
описание кластера, макросы, связь с keeper, `ReplicatedMergeTree`,
|
||||
`Distributed`, очередь распределённых DDL и очистку временных таблиц.
|
||||
`Distributed`, очередь распределённых DDL и очистку временных таблиц. Последняя
|
||||
из девяти проверок — единственная на настоящих данных: она подневно сверяет
|
||||
события стартового мира с описью и при расхождении говорит, где искать —
|
||||
в событиях или в браке.
|
||||
|
||||
`make config-test` проверяет Compose, синтаксис файлов DAG и пробельные ошибки
|
||||
в diff без запуска стенда.
|
||||
@@ -119,6 +127,38 @@ Kafka по той же причине спрашивают снаружи. Её
|
||||
полного сброса с удалением всех именованных томов используйте `make clean`.
|
||||
Повторный `make up` безопасен: одноразовая подготовка приложений идемпотентна.
|
||||
|
||||
## Стартовый мир
|
||||
|
||||
Стенд поднимается не пустым: разовая служба `world-init` играет в топик `hits`
|
||||
первые восемь дней модельного времени — понедельник по понедельник, 401 185
|
||||
событий. Дальше их обычным путём разбирает хранилище, и к концу `make up` они
|
||||
лежат в `ods.event`. Так у всякой лабы есть данные, и всегда одни и те же.
|
||||
|
||||
Ждать приходится дольше, чем работает заливка: приём асинхронный, поэтому
|
||||
вторым шагом `make up` зовёт `scripts/wait-for-world.sh` — тот опрашивает
|
||||
ClickHouse, пока мир не доедет. Повторный `make up` заливает мир заново; это
|
||||
не ошибка, а свойство: номера событий те же, и повтор схлопнет
|
||||
`ReplacingMergeTree`.
|
||||
|
||||
Сам мир в git не хранится — он чистая функция зерна, и держать его в
|
||||
репозитории значило бы держать там кэш. Вместо него лежит **опись мира**,
|
||||
[`data/world-inventory.json`](data/world-inventory.json): зерно, версия
|
||||
генератора, хеш каталога товаров и по строке на каждый день — дата, число
|
||||
событий и хеш его байтов. Опись отвечает на единственный вопрос: тот ли это
|
||||
мир, что был вчера.
|
||||
|
||||
Спрашивают её двое. `make test` сверяет опись с тем, что собирается из кода
|
||||
сегодня: правка генератора меняет мир, и опись надо пересобрать —
|
||||
`make inventory`. `make check-clickhouse` сверяет с описью то, что доехало до
|
||||
`ods.event`, подневно. Хеш дня можно пересчитать и руками — это обычный
|
||||
`sha256sum` файла, который пишет файловый приёмник:
|
||||
|
||||
```bash
|
||||
uv run --project generator python -m clickstream_generator batch \
|
||||
--day 0 --file tmp/day0.jsonl
|
||||
sha256sum tmp/day0.jsonl
|
||||
```
|
||||
|
||||
## Как позвать генератор
|
||||
|
||||
События производит генератор из [`generator/`](generator/). Модельный день —
|
||||
@@ -160,6 +200,10 @@ make generate-live GENERATOR_DAY=3 GENERATOR_SPEED=1000
|
||||
docker compose --profile generator build generator
|
||||
```
|
||||
|
||||
Тот же образ несёт заливка стартового мира, поэтому свежим его держит и
|
||||
обычный `make up`: он собирает образы всего стенда, и генератор теперь среди
|
||||
них.
|
||||
|
||||
Разведено это нарочно: собрать образ и запустить контейнер — разные действия, и
|
||||
цель запуска, молча пересобирающая образ, стирает между ними границу. Что образ
|
||||
устарел, видно по собственному прогону — это обратная связь, а не ловушка.
|
||||
|
||||
Reference in New Issue
Block a user