Зачем Стенд поднимался пустым, и всякая приёмка следующих этапов начиналась с ручной заливки данных. Теперь `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
80 lines
4.3 KiB
Bash
Executable File
80 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Ждёт, пока стартовый мир доедет до ODS. Второй шаг `make up`, и вот зачем он.
|
||
#
|
||
# `docker compose up --wait` дожидается служб, в том числе успешно отработавшей
|
||
# заливки. Но «заливка кончилась» — это «события лежат в топике», а не «события
|
||
# в хранилище»: приём асинхронный. Движок Kafka копит блок и отдаёт его по
|
||
# размеру либо по `stream_flush_interval_ms` (умолчание 7,5 с), а стартовый мир —
|
||
# около 400 тыс. сообщений. Верни `make up` управление сразу — и `make
|
||
# check-clickhouse` следом покраснел бы по устройству, а не по поломке.
|
||
#
|
||
# Ждём ограниченным циклом с потолком, а не паузой наугад: пауза либо коротка на
|
||
# медленной машине, либо ворует минуту на быстрой.
|
||
#
|
||
# Условие ожидания — «всё доехало», а не «всё разобралось»: сумма событий и
|
||
# брака. Сломайся разбор — события уедут в `*_errors`, сумма сойдётся, ожидание
|
||
# кончится, и поломку назовёт `make check-clickhouse`. Ждать здесь одних годных
|
||
# событий значило бы висеть пять минут вместо внятного ответа.
|
||
set -Eeuo pipefail
|
||
|
||
readonly ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
readonly INVENTORY="${ROOT_DIR}/data/world-inventory.json"
|
||
readonly ATTEMPTS=100
|
||
readonly PAUSE_SECONDS=3
|
||
# Как часто отчитываться о ходе: молчащая минуту команда выглядит зависшей.
|
||
readonly REPORT_EVERY=5
|
||
|
||
read -r -a COMPOSE_CMD <<<"${COMPOSE_BIN:-docker compose}"
|
||
|
||
fail() {
|
||
printf 'ОШИБКА: %s\n' "$1" >&2
|
||
exit 1
|
||
}
|
||
|
||
query() {
|
||
"${COMPOSE_CMD[@]}" --project-directory "$ROOT_DIR" exec -T clickhouse-01 \
|
||
clickhouse-client --query "$1" </dev/null
|
||
}
|
||
|
||
expected="$(jq '[.days[].events] | add' "$INVENTORY")"
|
||
first_date="$(jq -r '.days | first | .date' "$INVENTORY")"
|
||
last_date="$(jq -r '.days | last | .date' "$INVENTORY")"
|
||
|
||
printf 'Ждём стартовый мир в ods.event: %s событий за %s — %s...\n' \
|
||
"$expected" "$first_date" "$last_date"
|
||
|
||
# Счёт без FINAL: здесь спрашивают «доехало ли», а не «сколько их на самом
|
||
# деле». Повторная заливка даст больше ожидаемого — и это тоже «доехало».
|
||
arrived_sql="
|
||
SELECT
|
||
(SELECT count() FROM ods.event_dist
|
||
WHERE EventDate BETWEEN '${first_date}' AND '${last_date}')
|
||
+ (SELECT count() FROM ods.event_errors_dist)
|
||
"
|
||
|
||
for ((attempt = 1; attempt <= ATTEMPTS; attempt++)); do
|
||
arrived="$(query "$arrived_sql" 2>/dev/null || true)"
|
||
if [[ "$arrived" =~ ^[0-9]+$ ]] && ((arrived >= expected)); then
|
||
if ((arrived > expected)); then
|
||
# Повторный `make up` заливает мир заново. Промолчи мы об этом —
|
||
# удвоенное число под «ЗЕЛЁНО» выглядело бы поломкой.
|
||
printf 'ЗЕЛЁНО: стартовый мир доехал; строк %s при ожидаемых %s —' \
|
||
"$arrived" "$expected"
|
||
printf ' мир заливали повторно, повтор схлопнет ReplacingMergeTree.\n'
|
||
else
|
||
printf 'ЗЕЛЁНО: стартовый мир доехал: %s строк.\n' "$arrived"
|
||
fi
|
||
exit 0
|
||
fi
|
||
if ((attempt % REPORT_EVERY == 0)); then
|
||
printf 'Доехало %s из %s, ждём дальше (%d с)...\n' \
|
||
"${arrived:-0}" "$expected" "$((attempt * PAUSE_SECONDS))"
|
||
fi
|
||
sleep "$PAUSE_SECONDS"
|
||
done
|
||
|
||
fail "стартовый мир не доехал за $((ATTEMPTS * PAUSE_SECONDS)) с:
|
||
доехало ${arrived:-0} из ${expected}. Смотрите журнал заливки
|
||
(docker compose logs world-init) и чтеца топика:
|
||
SELECT * FROM system.kafka_consumers"
|