# Проверки: карта целей Документ отвечает на два вопроса. Первый: что именно утверждает каждая цель `make` и сколько стоит её прогон. Второй, ради которого документ и заведён: куда положить новую проверку — так, чтобы это решалось по карте, а не чтением скриптов. Зона ответственности у документа одна — проверки. Что именно они стерегут, описано в других местах: устройство хранилища — в [storage.md](storage.md), замысел стенда — в спеке [«Боевой реализм стенда (v2)»](../specs/2026-07-30-stand-v2-realism.md). ## Ось: кого спрашивают Цели различаются не ценой, а тем, к кому обращён вопрос. 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` | Генератор делает то, что обещает; схема события остаётся объявленным контрактом, а собранное из неё [описание выгрузки](../formats/clickstream-event.md) — свежим | не нужен | 40 с | | `make config-test` | Compose разбирается, файлы DAG синтаксически целы, в 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](../adr/0004-resource-limits.md) разбирает ровно этот случай — оценка из спеки попала жёстким порогом в `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, раздел «Что проверено». Разбор Bash из `make config-test` срезан 6 августа 2026 года, и вместе с ним — двенадцать строк обхода репозитория со списком файлов. Проверка была сломана с рождения: `bash -n` со списком файлов разбирает только первый, остальные уходят ему в аргументы, — то есть из пяти скриптов проверялся один, и за всё время цели этого никто не заметил. Заводить её заново незачем: скрипты стенда запускаются с той же машины, и синтаксическая ошибка вылезает при первом же запуске с номером строки. Разбор файлов DAG остался и по другому основанию — их на машине не запускает никто.