fix(world): устранены расхождения после переноса на main

- Зачем:
  - свежий main добавил мониторинг и проявил устаревшие доводы, мёртвые настройки и слишком короткий тайм-аут DDL.
- Что:
  - документация приведена к текущему жизненному циклу мира и составу стенда;
  - удалены лишняя проверка и неиспользуемый YAML-якорь;
  - ожидание ClickHouse возвращено к 300 секундам, а отказы и параметры DockerOperator пояснены.
- Проверка:
  - `make config-test`;
  - `make lint`;
  - `make smoke` — 21/21.
This commit is contained in:
ddadmin committed 2026-08-21 23:38:45 +03:00
1 parent 7e6554507a
commit bca1ffa379
8 files changed
+45 -34

No files matched your search

+2 -2
View File
@@ -69,8 +69,8 @@ make check-services
`superset-init` завершаются с кодом 0. Первый обновляет схему Airflow, `superset-init` завершаются с кодом 0. Первый обновляет схему Airflow,
подготавливает администратора и два подключения к `clickhouse-01`. Второй подготавливает администратора и два подключения к `clickhouse-01`. Второй
обновляет Superset, создаёт администратора и импортирует подключение к обновляет Superset, создаёт администратора и импортирует подключение к
`clickhouse-02`. `clickhouse-02`. На ноутбуке в режиме энергосбережения приёмочный подъём с
С нуля подъём занимает около трёх минут, на живом стенде — около минуты. пустых томов занял около десяти минут, повторный на живом стенде — около трёх.
`make smoke` за секунды спрашивает, собран ли стенд: зависимости машины, `make smoke` за секунды спрашивает, собран ли стенд: зависимости машины,
здоровье контейнеров, устройство keeper, ответ Kafka с машины через отображённый здоровье контейнеров, устройство keeper, ответ Kafka с машины через отображённый
-4
View File
@@ -46,10 +46,6 @@ x-kafka-target: &kafka-target
<<: *kafka-common <<: *kafka-common
KAFKA_TOPIC: hits KAFKA_TOPIC: hits
x-orders-kafka-target: &orders-kafka-target
<<: *kafka-common
KAFKA_TOPIC: *orders-topic
x-world-starting-days: &world-starting-days "8" x-world-starting-days: &world-starting-days "8"
x-airflow-common: &airflow-common x-airflow-common: &airflow-common
+9 -2
View File
@@ -56,7 +56,9 @@ def _clickhouse_client():
password=connection.password, password=connection.password,
database=connection.schema or "default", database=connection.schema or "default",
connect_timeout=5, connect_timeout=5,
send_receive_timeout=60, # `ON CLUSTER` ждёт хосты до 180 с. Транспорт живёт дольше, чтобы
# ClickHouse сам назвал незавершённый хост вместо сетевого тайм-аута.
send_receive_timeout=300,
) )
@@ -97,7 +99,10 @@ def _generator(task_id: str, command: list[str]) -> DockerOperator:
"KAFKA_BOOTSTRAP_SERVERS": KAFKA_BOOTSTRAP_SERVERS, "KAFKA_BOOTSTRAP_SERVERS": KAFKA_BOOTSTRAP_SERVERS,
"KAFKA_TOPIC": HITS_TOPIC, "KAFKA_TOPIC": HITS_TOPIC,
}, },
# Вывод уже переехал в журнал задачи; завершённый контейнер не нужен.
auto_remove="force", auto_remove="force",
# Временный путь Airflow существует внутри контейнера, но монтировать
# его пытается хостовый Docker. Генератору этот каталог не нужен.
mount_tmp_dir=False, mount_tmp_dir=False,
) )
@@ -260,7 +265,9 @@ def world_initialize():
finally: finally:
client.close() client.close()
raise AirflowException( raise AirflowException(
f"стартовый мир не принят за 300 с: строк {arrived} из {expected}" f"стартовый мир не принят за 300 с: строк {arrived} из {expected}; "
"проверьте журнал send_initial_events и "
"SELECT * FROM system.kafka_consumers"
) )
@task @task
+6
View File
@@ -10,6 +10,12 @@
обработчик DAG. Triggerer не запускается: в каркасе нет отложенных задач. обработчик DAG. Triggerer не запускается: в каркасе нет отложенных задач.
Два интеграционных пробника написаны через публичный `airflow.sdk`. Два интеграционных пробника написаны через публичный `airflow.sdk`.
Долгоживущие процессы Airflow зависят от `airflow-init`, а Superset — от
`superset-init`. Это ребро нужно самому `up --wait`: Docker Compose 2.40.3
считает успешно завершившийся одноразовый сервис без зависимого упавшим и
возвращает единицу — поведение проверено при исполнении #37. Зависимость
включает результат подготовки в общий граф ожидания.
Для входа выбран SimpleAuthManager. У него нет команды создания пользователя, Для входа выбран SimpleAuthManager. У него нет команды создания пользователя,
поэтому `airflow-init` записывает пароль администратора из окружения в поэтому `airflow-init` записывает пароль администратора из окружения в
JSON-файл отдельного тома `airflow_auth`. Новый именованный том принадлежит JSON-файл отдельного тома `airflow_auth`. Новый именованный том принадлежит
+4 -2
View File
@@ -144,8 +144,10 @@ Keeper — отдельный сервис, системных таблиц у
вышло восемь: четыре таблицы, каждая обеими нодами. Таблиц ошибок среди них вышло восемь: четыре таблицы, каждая обеими нодами. Таблиц ошибок среди них
нет, и это правда состояния — частей у них пока не появилось. нет, и это правда состояния — частей у них пока не появилось.
- Очередь DDL показала `CREATE MATERIALIZED VIEW ... ON CLUSTER` в состоянии - Очередь DDL показала `CREATE MATERIALIZED VIEW ... ON CLUSTER` в состоянии
`Finished` и со временем по каждой ноде: `make generate-batch` поднимает `Finished` и со временем по каждой ноде. Новые безопасные записи для показа
`clickhouse-init`, и тот заново прогоняет DDL. оставляет `make check-clickhouse`: цель создаёт и удаляет временные таблицы
`ON CLUSTER`. Канонический DDL появляется в очереди при создании мира дагом
`world_initialize`.
Подписи рядов (`${__field.labels.node}`) и правило, по которому пунктирным Подписи рядов (`${__field.labels.node}`) и правило, по которому пунктирным
становится только ряд «отведено», сверены по документации Grafana через MCP становится только ряд «отведено», сверены по документации Grafana через MCP
+16 -12
View File
@@ -241,22 +241,26 @@ Kafka → STG → ODS, и живёт он в `make check-clickhouse`.
19/2: назвал нездоровый сервис и отсутствие одной из четырёх целей. После 19/2: назвал нездоровый сервис и отсутствие одной из четырёх целей. После
возврата сервиса прогон снова дал 21/21. возврата сервиса прогон снова дал 21/21.
**Приёмка #119 21 августа 2026 года.** `make rebuild-storage` через Airflow После переноса #119 поверх этого состава повторный `make up` сохранил готовый
прошёл за 3 м 9 с: позиция прошла задачи `-1` → `0` → `8`, топики получили мир, смоук прошёл 21/21, проверка ClickHouse — 10/10, межсервисная — 7/7.
новые идентификаторы, группы дочитали новые топики без отставания, а ODS принял
401 185 событий без брака и 2 851 актуальный заказ. Повторный `make up` занял **Приёмка логики #119 до переноса поверх мониторинга 21 августа 2026 года.**
2 м 41 с вместе со штатным повторным запуском служб инициализации; `make rebuild-storage` через Airflow прошёл за 3 м 9 с: позиция прошла задачи
`world_initialize` завершился за один цикл опроса и оставил счётчики `-1` → `0` → `8`, топики получили новые идентификаторы, группы дочитали новые
неизменными. Отдельно топики без отставания, а ODS принял 401 185 событий без брака и 2 851
проверены красные отказы `world_initialize` при `-1` и `world_next_day` при актуальный заказ. Задача `build_generator_image` заняла по 1,5 с в двух
отсутствующей позиции, `0` и `-1`; `lifecycle` отказано в чтении прогонах.
`system.users`, но разрешено чтение `system.settings`. Повторный `make up` занял 2 м 41 с вместе со штатным повторным запуском служб
инициализации; `world_initialize` завершился за один цикл опроса и оставил
счётчики неизменными. Отдельно проверены красные отказы `world_initialize` при
`-1` и `world_next_day` при отсутствующей позиции, `0` и `-1`; `lifecycle`
отказано в чтении `system.users`, но разрешено чтение `system.settings`.
Итоговый холодный `make up` из пустых томов занял 9 м 39 с после 25 с на Итоговый холодный `make up` из пустых томов занял 9 м 39 с после 25 с на
`make clean`. Даг создал отсутствующую позицию и закончил на `8`; ODS снова `make clean`. Даг создал отсутствующую позицию и закончил на `8`; ODS снова
получил 401 185 событий без брака и 2 851 заказ. У `hits` два раздела, у получил 401 185 событий без брака и 2 851 заказ. У `hits` два раздела, у
`orders` один, отставание обеих групп равно нулю. На этом стенде прошли смоук `orders` один, отставание обеих групп равно нулю. На тогдашнем составе стенда
20/20, проверка ClickHouse 10/10 и межсервисная проверка 7/7. прошли смоук 20/20, проверка ClickHouse 10/10 и межсервисная проверка 7/7.
Оборванную очистку проверили после записи `world_position=-1`: остановили Оборванную очистку проверили после записи `world_position=-1`: остановили
первую ноду ClickHouse, и `world_recreate` покраснел на удалении баз. Следующий первую ноду ClickHouse, и `world_recreate` покраснел на удалении баз. Следующий
+8 -7
View File
@@ -326,10 +326,10 @@
- **Обрыв любого режима — переигровка дня целиком**; дедуп склеивает - **Обрыв любого режима — переигровка дня целиком**; дедуп склеивает
повторы: `WatchID` детерминированы, повтор — та же строка для повторы: `WatchID` детерминированы, повтор — та же строка для
ReplacingMergeTree. ReplacingMergeTree.
- **Эталонный снимок при старте стенда — через Kafka, пакетным режимом - **Эталонный снимок при создании мира — через Kafka, пакетным режимом
проигрывателя.** Отдельный механизм заливки не строится: каждый `make up` проигрывателя.** Отдельный механизм заливки не строится: `world_initialize`
бесплатно прогоняет весь конвейер на настоящих данных, и строгий приём прогоняет весь конвейер на настоящих данных при первом `make up` и при явном
хранилища проверяет контракт тем же прогоном. `make rebuild-storage`. На готовом мире повторный `make up` заливку пропускает.
Оговорка «если заливка уйдёт в десятки минут — вернуться к прямой Оговорка «если заливка уйдёт в десятки минут — вернуться к прямой
загрузке» проверена при фиксации чисел: 14 × 50 тыс. ≈ 700 тыс. событий — загрузке» проверена при фиксации чисел: 14 × 50 тыс. ≈ 700 тыс. событий —
расчётно минута-две, запас есть. расчётно минута-две, запас есть.
@@ -361,10 +361,11 @@
окном K = 7, дышащая зона и две волны недельной сезонности. Удлинение до окном K = 7, дышащая зона и две волны недельной сезонности. Удлинение до
месяца — дешёвый ход (пересборка описи), если понадобится. месяца — дешёвый ход (пересборка описи), если понадобится.
- **В git — только опись, снимок не хранится.** Снимок генерируется при - **В git — только опись, снимок не хранится.** Снимок генерируется при
`make up` и при проверках: артефакт — кэш чистой функции, кэш в git не создании мира и в проверках генератора: артефакт — кэш чистой функции, кэш в
хранят. Опись несёт паспорт мира, счётчики и хеши по дням; проверки git не хранят. Опись несёт паспорт мира, счётчики и хеши по дням; проверки
«пустой git diff» и «пересгенерируй день N — сравни хеш» живут на ней. «пустой git diff» и «пересгенерируй день N — сравни хеш» живут на ней.
Каждый `make up` — живая демонстрация детерминизма. Честная потеря — Первый `make up` пустого стенда — живая демонстрация детерминизма; готовый мир
повторный подъём не пересчитывает. Честная потеря —
страховка на случай платформенного бага: раньше менти с расходящимися страховка на случай платформенного бага: раньше менти с расходящимися
байтами мог взять готовый снимок из git, теперь он упрётся в красный чек байтами мог взять готовый снимок из git, теперь он упрётся в красный чек
описи; смягчение — CI гоняет генерацию на amd64 и arm64. описи; смягчение — CI гоняет генерацию на amd64 и arm64.
-5
View File
@@ -62,11 +62,6 @@ jq -e '
["kafka-init", "clickhouse-init", "world-init", "world-snapshots"] | ["kafka-init", "clickhouse-init", "world-init", "world-snapshots"] |
all(. as $name | $services | has($name) | not) all(. as $name | $services | has($name) | not)
' >/dev/null <<<"$config_json" ' >/dev/null <<<"$config_json"
jq -e '
.services.generator as $generator |
((($generator.depends_on // {}) | has("kafka-init")) | not) and
((($generator.depends_on // {}) | has("clickhouse-init")) | not)
' >/dev/null <<<"$config_json"
jq -e ' jq -e '
.services["airflow-scheduler"] as $service | .services["airflow-scheduler"] as $service |
($service.environment.CLICKHOUSE_LIFECYCLE_PASSWORD | length > 0) and ($service.environment.CLICKHOUSE_LIFECYCLE_PASSWORD | length > 0) and