- Зачем:
- деление целей оставило дыру: про Airflow, Superset, Prometheus и Grafana
смоук стучится с машины в отображённый порт, а про Kafka после переезда
check_kafka_from_host знал только «контейнер здоров».
- вердикт этот приходит из healthcheck в compose.yaml, а тот спрашивает
брокер изнутри и по внутреннему слушателю: объявленный наружу адрес может
вести не туда, и Kafka всё равно останется здоровой.
- поломка популярная и показательная: клиент подключается, получает
метаданные и молча виснет на адресе, которого с его стороны нет. Менти
узнаёт, что у брокера два слушателя и зачем нужен advertised.listeners.
Генератор будет писать в Kafka именно с машины.
- Что:
- check_kafka_external_listener в make smoke: запрос списка топиков с машины
через отображённый порт, ответ приходит только если объявленный адрес ведёт
туда же. Комментарий у проверки объясняет, от чего она заведена.
- ожидание ответа ограничено 15 секундами при замеренных 2,6 — впятеро
больше, чем стоит зелёный прогон.
- README и карта проверок: новая проверка названа, доводы записаны, цена
смоука обновлена с 6 до 8 секунд.
- Проверка:
- make config-test, make smoke (20 проверок, 8 с), make check-clickhouse,
make check-services — зелёные.
- краснеет на своей поломке: брокеру объявлен адрес kafka-nowhere:29092 при
целом внутреннем слушателе — проверка состояния контейнера осталась
зелёной, смоук покраснел именно на этой строке. После проверки Kafka
возвращена в исходное состояние, посторонних контейнеров не осталось.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
14 KiB
Проверки: карта целей
Документ отвечает на два вопроса. Первый: что именно утверждает каждая цель
make и сколько стоит её прогон. Второй, ради которого документ и заведён:
куда положить новую проверку — так, чтобы это решалось по карте, а не чтением
скриптов.
Зона ответственности у документа одна — проверки. Что именно они стерегут, описано в других местах: устройство хранилища — в storage.md, замысел стенда — в спеке «Боевой реализм стенда (v2)».
Ось: кого спрашивают
Цели различаются не ценой, а тем, к кому обращён вопрос. ClickHouse отвечает сам и за миллисекунды. Airflow отвечает через такт планировщика. Superset — через сессию и обход метаданных. Быстрота выходит следствием этого различия, а не критерием деления.
Совпадение цены и назначения здесь побочное, и это видно по дырам, которые
каждая цель оставляет соседке. make smoke не заметит перепутанных местами
макросов shard: обе ноды здоровы и порты отвечают. make check-clickhouse не
заметит потерянного подключения Superset: он про ClickHouse и только.
Отсюда правило для новой проверки: спроси, кого она спрашивает. Договор со
схемой событий — вопрос к ClickHouse, значит дом ему в check-clickhouse, даже
если по цене он подошёл бы смоуку. Счётчики против манифеста — тоже вопрос к
ClickHouse: строки в ods.event считает сам сервер и отвечает сразу.
Карта целей
Стенд нужен трём целям из семи. Цена — замер 6 августа 2026 года, см. «Что проверено».
| Цель | Что утверждает | Стенд | Цена |
|---|---|---|---|
make lint |
Код генератора отформатирован и проходит ruff | не нужен | 0,4 с |
make typecheck |
Типы генератора сходятся (ty) | не нужен | 0,5 с |
make test |
Генератор делает то, что обещает; схема события остаётся объявленным контрактом, а собранное из неё описание выгрузки — свежим | не нужен | 40 с |
make config-test |
Compose разбирается, Bash и Python синтаксически целы, в diff нет пробельных ошибок. О работоспособности не говорит ничего | не нужен | 1 с |
make smoke |
Стенд собран: службы живы, порты отвечают, подключения настроены друг на друга. Вширь и по касательной к каждой службе. Единственная цель, которая здесь правда смоук | нужен | 8 с |
make check-clickhouse |
Всё, что спрашивают у ClickHouse и он отвечает сам: макросы, шарды, реплики, путь в keeper, ключ шардирования, очередь распределённых DDL | нужен | 7 с |
make check-services |
Службы работают: DAG запускается и доходит, топик создаётся и удаляется, Superset логинится и ходит в базу | нужен | 44 с |
Правило: смоук обязан оставаться быстрым
make smoke — быстрая проверка для регулярного прогона: её гоняют не
задумываясь, и потому она обязана укладываться в секунды. Тяжёлые проверки в
неё включать не следует: тяжёлая — та, из-за которой смоук перестаёт быть
быстрым. Поодиночке это единицы и десятки секунд, вместе — минуты. Такие
проверки живут в make check-services.
На практике дорого обходится не работа, а ожидание службы: такт планировщика
Airflow, сессия Superset. make check-clickhouse создаёт таблицы, вставляет
строки и гоняет распределённые DDL — и укладывается в семь секунд, потому что
ClickHouse отвечает сразу.
Первый абзац — правило, второй — наблюдение, по которому тяжёлую проверку узнают заранее, не замеряя.
Какую проверку когда запускать
Деление целей ценно ровно до тех пор, пока оно не превратилось в «гонять всегда всё».
| Когда | Что гонять |
|---|---|
| Правка в работе | make config-test и make smoke |
| PR или задача | плюс цели, которых правка касалась: DAG-и, Superset или Kafka — check-services; DDL, кластер или данные — check-clickhouse |
| Приёмка этапа | make up с нуля и все три цели на стенде |
Правки генератора добавляют к этому make lint, make typecheck и
make test: стенд им не нужен, а make test из них самая дорогая.
Порогов по времени здесь нет
Цена в таблице — замеренное число с датой замера, а не назначенный порог.
Автоматической проверки времени в репозитории нет и заводить её не следует.
Число, вписанное в проверку, становится законом, которого никто не выбирал:
ADR 0004 разбирает ровно этот случай — оценка
из спеки попала жёстким порогом в make smoke, make smoke стал критерием
приёмки каждого этапа, и дальше решения сверялись уже с порогом, а не с
исходным доводом. Спека генератора формулирует ту же позицию прямо:
«наблюдаемость без порогов».
Смотрит на время человек. make smoke и make check-services печатают его
сами — последней строкой ИТОГ: пройдено N, ошибок M, время T с. Замер при
приёмке делается руками и называется в теле PR.
Проверка, которая не умеет краснеть, бесполезна
Правило, которое стоит держать в голове, правя любую проверку. Проверка,
никогда не видевшая своей поломки, доказывает только то, что она умеет печатать
«ЗЕЛЁНО». Убедиться дешевле всего руками: сломайте то, что она стережёт —
остановите prometheus, удалите служебную таблицу пробника на второй ноде, — и
посмотрите, покраснеет ли прогон и назовёт ли виновника. Не покраснел —
проверка не работает, и чинить надо её, а не стенд.
Устройство скриптов
| Цель | Скрипт |
|---|---|
make config-test |
scripts/config-test.sh |
make smoke |
scripts/stand-smoke.sh |
make check-services |
scripts/stand-services.sh |
make check-clickhouse |
scripts/clickhouse-smoke.sh |
Имя clickhouse-smoke.sh осталось от прежнего имени цели — smoke-cluster.
Файл переименуют при следующем касании: сейчас в него встраивается проверка
договора со схемой, и переименование устроило бы конфликт на ровном месте.
Общее у смоука и check-services — счёт проверок, обращение к Compose и две
проверки — вынесено в scripts/stand-common.sh; сам он не запускается.
Оттуда же приходят два решения, которые видно по счёту прогонов:
check_containers_survivedстоит в конце обеих целей. Убитый за память контейнер Docker поднимает сам, и проверка здоровья об этом промолчит (ADR 0004). Стенд нагружаетcheck-services, а увидеть последствия нужно и тому, кто гонял один смоук. Это единственная проверка, которая считается дважды: 20 у смоука плюс 7 уcheck-services— это 26 разных проверок.- Зависимости машины считает только смоук. «На машине есть Docker, curl и
jq» — вопрос к машине, а не к службам, и на оси он стоит рядом с «стенд
собран». Для
check-servicesэто условие запуска: без них он не начнёт работу и громко скажет об этом, но ЗЕЛЁНО за это не печатает.
Вход в Airflow check-services выполняет заново — общих переменных у двух
скриптов нет. Отдельной проверкой этот вход тоже не считается: то же самое
утверждает смоук.
Что проверено
Замеры 6 августа 2026 года, стенд поднят заранее; время make up в цену целей
не входит. Время взято по time и совпадает с тем, что цель печатает сама. Оно
плавает от прогона к прогону: смоук дал 8 секунд дважды (без проверки Kafka,
до её появления, — 5 и 6), check-services — 43 и 44. В таблице стоит большее
из замеренных.
До деления scripts/stand-smoke.sh шёл 48 секунд на 25 проверок, из них
42 секунды съедали шесть: Kafka с машины, два запуска пробников Airflow и три
проверки Superset. После деления те же 25 проверок разошлись по двум целям:
19 в смоуке и 6 в check-services. Содержание ни одной из них не менялось.
Двадцатая проверка смоука — единственная новая: Kafka спрашивают с машины через
отображённый порт. Деление оставило дыру, которой раньше не было. Про Airflow,
Superset, Prometheus и Grafana смоук стучится с машины в отображённый порт, а
про Kafka после переезда знал только «контейнер здоров» — а это вердикт
проверки состояния из compose.yaml, и та спрашивает брокер изнутри по
внутреннему слушателю. Внешняя дверь оставалась непроверенной до
check-services с его 44 секундами, хотя генератор пишет в Kafka именно с
машины. Стоит проверка 2,6 секунды, и почти всё это — старт JVM в разовом
контейнере; ожидание ответа ограничено пятнадцатью секундами, впятеро больше
замеренного.
Что обе разделённые цели умеют краснеть, проверено руками в тот же день. Со
снятым prometheus смоук дал три ошибки и ненулевой код возврата. С
подменённым ожидаемым UUID подключения Superset так же покраснел
check-services. Новую проверку Kafka проверили её собственной поломкой:
брокеру объявили адрес kafka-nowhere:29092, оставив внутренний слушатель
целым, — проверка состояния контейнера осталась зелёной, а смоук покраснел
именно на этой строке. Краснеют они и когда на машине не хватает команды из
списка зависимостей.
Семантика счётчиков Docker OOMKilled и RestartCount, на которой держится
check_containers_survived, снята отдельными контейнерами и записана в
ADR 0004, раздел «Что проверено».