- Зачем:
- устранены дублирование значений и тихая подстановка неполной настройки.
- Что:
- версии образов и внутренняя топология закреплены рядом с местом использования.
- имя экземпляра, внешние порты, учётные данные и ключи сделаны обязательными настройками .env.
- быстрый старт, Dockerfile и статическая проверка приведены к новой границе.
- Проверка:
- make config-test.
- docker build для образов Airflow и Superset без аргументов.
- make up; make smoke; make check-clickhouse; make check-services.
- Зачем:
- до сих пор генератор умел собирать день, но не умел его отдать: топик
hits наполнялся пробником, а не настоящими данными. Тикет #41 доводит
события до стенда и закрывает форму на проводе, на которую обопрётся
типизированный ODS (#43).
- сериализатор один по решению спеки: второе место, печатающее событие в
JSON, разошлось бы с первым молча.
- Что:
- serialize.py — канонический сериализатор на orjson: единственное место,
где событие целиком становится JSON; 47 ключей всегда, «пусто» это
пустое значение, даты ISO-8601, ecommerce строкой. Вложенный блок
ecommerce в commerce.py вторым сериализатором не считается — правило
про событие, а не про блок внутри него.
- sinks.py — приёмники: файл (одно событие — одна строка) и Kafka (одно
событие — одно сообщение). Ключа у сообщения нет: WatchID уникален,
ключом он был бы ключом лишь на вид.
- player.py, cli.py — проигрыватель и интерфейс запуска: режимы batch и
live (темп ×60), несколько дней одним запуском, ограниченная пачка,
раздельные тайминги генерации и доставки, лаг в логе.
- день на оси и имя топика умолчаний не имеют: параметр, описывающий
среду или позицию, приходит от зовущего, иначе отказ до генерации.
Умолчания зерна, числа дней и темпа остаются — они описывают мир.
- generator/Dockerfile — свой образ: зависимости из uv.lock, база
закреплена до патча, раскладка репозитория сохранена ради каталога
товаров. Образ Airflow не тронут.
- разовая служба compose под профилем, цели generate-batch и
generate-live, .dockerignore, tmp/ в .gitignore.
- решения внесены в спеку (разделы 4, 8, 9), быстрый старт — в README.
- Проверка:
- make test 406 passed, make lint, make typecheck, make config-test.
- побайтовый детерминизм: два прогона дня в независимых процессах дают
один sha256; день в контейнере совпадает с днём на машине.
- на стенде: пакетный день доехал до stg.hits_raw_dist, счёт по
Distributed сошёлся — отправлено 50626, в таблице 50626.
- топик прочитан обеими нодами: clickhouse-01 раздел 0 (26368),
clickhouse-02 раздел 1 (24258).
- живой день: модельное время 01:00 на 60-й секунде, 02:00 на 120-й —
темп ×60, лаг печатается.
- форма на проводе в колонке raw: даты читаются глазами, ecommerce лежит
строкой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Зачем:
- проверка была сломана с рождения цели: bash -n со списком файлов
разбирает только первый, остальные уходят ему в аргументы. Из пяти
скриптов проверялся один, и за всё время этого никто не заметил.
- чинить незачем: скрипты стенда запускают с той же машины, и
синтаксическая ошибка вылезает при первом же запуске с номером строки.
Учебной ценности в проверке нет — из неё не узнаёшь ничего, кроме того,
что у bash есть ключ -n.
- держалась она не строчкой, а двенадцатью: обход репозитория, временный
файл со списком, mapfile и две ветки на пустой список.
- Что:
- из scripts/config-test.sh убраны разбор Bash и весь аппарат сбора
списка файлов; 53 строки стали 40.
- разбор файлов DAG остался и получил комментарий с основанием: их на
машине не запускает никто, обработчик разбирает их внутри контейнера, и
ошибка всплывает не сообщением, а молча пропавшим DAG.
- README и карта проверок больше не обещают проверку синтаксиса Bash.
- в карте записано, почему проверку не стоит заводить заново.
- Проверка:
- make config-test зелёный.
- оставшийся разбор DAG краснеет: незакрытая скобка в dags/test_kafka.py
роняет цель с SyntaxError и ненулевым кодом; файл восстановлен.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Зачем:
- проверок стало больше, чем продукта, и росли они из критериев приёмки,
а не из учебной ценности (#56).
- Что:
- удалены tests/dag-probes-unit.py, tests/stand-smoke-guards.sh,
tests/stand-smoke-static.sh и tests/smoke-guards.sh вместе с целью
make smoke-guards и запуском юнит-тестов в scripts/config-test.sh.
- из scripts/stand-smoke.sh убран check_env_consistency, туда же переехал
check_keeper_runtime; счёт проверок остался 25.
- в пробниках свёрнуты функции _assert_*, комментарий про отложенный импорт
переписан на причину из документации Airflow и продублирован в test_kafka.
- Проверка:
- make config-test, make up, make smoke, make smoke-cluster.
Зачем.
Демонстрационный DAG example_clickstream_hello ничего не проверял: он не
обращался ни к ClickHouse, ни к Kafka, поэтому его зелёный результат ничего
не говорил о стенде. Пробники проверяют связи по-настоящему — и тем же
клиентом, каким будут ходить рабочие DAG.
Что.
- test_clickhouse: пишет строку в ReplicatedMergeTree на ноде 1 и читает её
с ноды 2 через Distributed. Данные проходят путь «нода 2 → все шарды →
шард ноды 1», то есть проверяется межшардовое чтение, а не одна нода.
- test_kafka: пишет в постоянный топик сообщение с меткой прогона и
вычитывает его обратно.
- infra/airflow/Dockerfile: clickhouse-connect 1.6.0 и confluent-kafka
2.15.0 вшиты в образ, импорт проверяется на сборке — при запуске
контейнера пакеты не доустанавливаются.
- Проверки: scripts/stand-smoke.sh гоняет оба пробника через API Airflow,
scripts/config-test.sh разбирает DAG без стенда,
tests/stand-smoke-guards.sh проверяет красный путь,
tests/dag-probes-unit.py — модульные проверки разбора.
- README и ADR 0001 обновлены тем же изменением.
- Удалён dags/example_clickstream_hello.py.
Проверка.
make config-test — пройдено 3, 3 и 6, ошибок 0.
make clean; cp .env.example .env; make up — 116 с на чистых томах.
make smoke — пройдено 25, ошибок 0; стенд занимает 2244,0 MiB.
make smoke-cluster — все 8 проверок кластера.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Зачем: рядом с кластером ClickHouse не хватало остальной платформы, а
поднимать её по кускам — значит каждый раз вспоминать порядок. Теперь
`make up` даёт стенд целиком, а `make smoke` честно отвечает, работает он
или нет.
Что:
- Kafka в режиме KRaft (без ZooKeeper), Postgres под метаданные, Airflow
3.3 четырьмя сервисами и Superset 6.1 с драйверами ClickHouse;
- мониторинг: Prometheus снимает метрики с обеих нод ClickHouse и keeper,
Grafana получает подготовленный источник данных;
- подключение Airflow ведёт на ноду 1, подключение Superset — на ноду 2:
ловушка правильных ошибок, забытый `ON CLUSTER` виден в дашборде сам;
- `scripts/stand-smoke.sh` — сквозная проверка из 24 пунктов: топик в
Kafka, цели Prometheus, запуск примера DAG через API Airflow, проверка
подключения Superset и расход памяти против порога 3,4 ГБ;
- `make config-test` — статические ворота: Compose, синтаксис Bash и
Python, стражи README; стражи smoke проверяют, что отчёт краснеет на
сломанном стенде и зеленеет после восстановления;
- решения записаны в `docs/adr/0001-stand-services.md`, состав стенда и
порядок работы — в README.
Проверка: на чистых томах `make clean` → `cp .env.example .env` →
`make up` (1 мин 51 с) → `make smoke` — 24 пройдено, 0 ошибок, 2171 MiB.
Перезапуск `make down` → `make up` → `make smoke` — 24/0. Также зелены
`make config-test` (3/0 и 5/0), `make smoke-cluster` (8/8) и
`make smoke-guards` (3/0 и 3/0).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>