docs(world): описан жизненный цикл прикладного мира
- Зачем: - границы владения Compose и Airflow должны быть видны менти из одного источника истины. - Что: - добавлены спека и ADR жизненного цикла мира; - обновлены README, архитектурные справочники и прежние решения; - правило полной очистки записано как явное и проверяемое требование к агенту. - Проверка: - документация вычитана через `ai-text-lint` и `writing-for-agents`; - выполнена литературная вычитка русского текста; - `git diff --check`.
This commit is contained in:
1 parent
fc5244cc67
commit
7e6554507a
12 files changed
+499
-104
No files matched your search
@@ -24,9 +24,11 @@ JSON-файл отдельного тома `airflow_auth`. Новый имен
|
||||
пользователи и строки подключения. Это экономит один контейнер, но сохраняет
|
||||
границу владения данными.
|
||||
|
||||
Airflow получает подготовленное общее подключение к `clickhouse-01`, а
|
||||
Superset — подключение `clickhousedb://` к `clickhouse-02`. Провайдер
|
||||
ClickHouse для Airflow не нужен: локальный образ содержит прямой клиент.
|
||||
Airflow получает два подготовленных подключения к `clickhouse-01`: рабочее
|
||||
`clickhouse_default` и административное `clickhouse_lifecycle` для дагов
|
||||
жизненного цикла мира ([ADR 0013](0013-world-lifecycle.md)). Superset получает
|
||||
подключение `clickhousedb://` к `clickhouse-02`. Провайдер ClickHouse для
|
||||
Airflow не нужен: локальный образ содержит прямой клиент.
|
||||
Пробник создаёт служебные таблицы `ON CLUSTER`, пишет на первой ноде и через
|
||||
`remote` читает `Distributed` на второй. Разные ноды сохраняют учебную
|
||||
ловушку: забытый `ON CLUSTER` проявится в Superset, даже если операция Airflow
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
|
||||
- `default` — с паролем, только служебный: проверки здоровья нод и работа
|
||||
изнутри контейнеров. Приложения им не ходят.
|
||||
- `etl` с ролью `etl_writer` — чтение и запись во всех слоях. Им ходит
|
||||
Airflow и применяется DDL при подъёме стенда.
|
||||
- `etl` с ролью `etl_writer` — чтение и запись во всех слоях. Им ходят рабочие
|
||||
даги Airflow.
|
||||
- `lifecycle` с ролью `lifecycle_owner` — создание и удаление только
|
||||
прикладных баз и объектов. Им даги жизненного цикла применяют DDL и
|
||||
пересоздают мир ([ADR 0013](0013-world-lifecycle.md)).
|
||||
- `bi` с ролью `bi_reader` — чтение витрин DM, слоя DDS и справочников `dic`.
|
||||
Им ходит Superset.
|
||||
- `analyst` с ролью `analyst_reader` — чтение всех слоёв и справочников. Им
|
||||
@@ -127,10 +130,11 @@ DDS под ними, потому что так чаще всего и быва
|
||||
Единственное, что ему доступно сегодня, — справочники: с ними зона `dic`
|
||||
пришла раньше своих потребителей.
|
||||
|
||||
Владелец матвью приёма определяется тем, кто применил DDL. Весь DDL стенда
|
||||
идёт через `IF NOT EXISTS`, поэтому на существующих томах три матвью
|
||||
сохранят нынешнего владельца, а на собранном заново стенде им станет `etl`.
|
||||
Работают оба — права есть у обоих, — но два стенда в этом расходятся.
|
||||
DDL для матвью приёма выполняется от имени `lifecycle`. Это видно в журнале
|
||||
запросов, а при создании ClickHouse проверяет у этой учётки чтение источника и
|
||||
вставку в цель. Так отдельная учётка показывает границу между редким
|
||||
управлением структурой и повседневной записью `etl`, не выдавая рабочим дагам
|
||||
право удалять базы.
|
||||
|
||||
Лаба «сломать права» вариантом с файлами не закрывается: создать через SQL
|
||||
новую роль и нового пользователя ничто не мешает, и новую роль разрешено
|
||||
@@ -182,8 +186,8 @@ ClickHouse через MCP Context7 (#114).
|
||||
- Секрет кластера принимается внутри `remote_servers` в виде
|
||||
`<secret from_env="CLICKHOUSE_CLUSTER_SECRET"/>`. Запрос от `etl` через обе
|
||||
ноды выполнился на каждой под пользователем `etl`.
|
||||
- `from_env` работает у элемента `password`: `etl`, `bi` и `analyst` вошли с
|
||||
паролями из окружения и получили свои роли.
|
||||
- `from_env` работает у элемента `password`: `etl`, `lifecycle`, `bi` и
|
||||
`analyst` вошли с паролями из окружения и получили свои роли.
|
||||
- При запросе от `bi` через Distributed на второй ноде `system.query_log`
|
||||
показал `user = bi` и `initial_user = bi`. Значит, на соседнем шарде
|
||||
применяются права инициатора, а не общей учётной записи.
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
работник берёт из переменной, а не из логической даты прогона: календарь Airflow
|
||||
к оси мира отношения не имеет.
|
||||
|
||||
Позднее [ADR 0013](0013-world-lifecycle.md) отдал Airflow начальную позицию и
|
||||
состояния обслуживания: переменной нет до первого создания мира, `-1` означает
|
||||
незавершённую очистку, `0` — чистый мир до генерации, а работники принимают
|
||||
только позицию `8` и больше.
|
||||
|
||||
Пакетная автоматика — второй выключатель, дёргающий `world_next_day`, —
|
||||
**отложена, а не отвергнута**.
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# ADR 0013. Жизненный цикл мира ведёт Airflow
|
||||
|
||||
Дата: 21 августа 2026 года. Статус: принято. Реализация — тикет #119.
|
||||
|
||||
## Решение
|
||||
|
||||
Compose поднимает службы стенда. Airflow владеет прикладным миром: топиками
|
||||
Kafka, DDL прикладных баз ClickHouse, стартовыми данными и позицией модельного
|
||||
времени. `make` поднимает инфраструктуру и через REST API запускает нужный даг,
|
||||
дожидаясь его результата.
|
||||
|
||||
Безопасный даг `world_initialize` создаёт мир, если тот ещё не создан. На
|
||||
готовом мире он ничего не меняет и успешно сообщает об этом в журнале.
|
||||
Разрушительный даг `world_recreate` очищает прикладные базы, топики и позиции
|
||||
их потребителей, затем запускает `world_initialize` и ждёт его завершения.
|
||||
Канонический DDL применяет отдельная учётная запись Airflow с правами только на
|
||||
прикладную структуру стенда.
|
||||
|
||||
`world_position` остаётся единственным состоянием пульта:
|
||||
|
||||
- переменной нет — стенд ещё не инициализировали;
|
||||
- `-1` — прежний мир признан недействительным, очистка не завершена;
|
||||
- `0` — мир очищен и готов к начальной генерации;
|
||||
- `8` и больше — первый несыгранный день готового мира.
|
||||
|
||||
`world_recreate` ставит `-1` до первого удаления и `0` только после успешной
|
||||
очистки. `world_initialize` принимает отсутствие позиции или `0`, а значение
|
||||
`8` и больше считает готовым миром. Позиция становится `8` последней задачей,
|
||||
после генерации стартовых дней и успешного приёма обоих источников. Обычные
|
||||
даги движения мира работают только с готовой позицией.
|
||||
|
||||
## Почему
|
||||
|
||||
Прежняя цепочка делила один процесс между Compose, shell-скриптами и Airflow:
|
||||
одноразовые службы создавали стартовые данные, отдельный сценарий ждал их
|
||||
приёма, а пересборка сама исправляла внутреннюю переменную пульта. Из-за этой
|
||||
границы успешная отправка в Kafka выдавалась за создание мира, повторный
|
||||
`make up` смешивался с инициализацией данных, а узкая пересборка сохраняла в
|
||||
Kafka сообщения и офсеты прежнего мира.
|
||||
|
||||
Airflow уже ведёт последующие дни и приём заказов. Отдав ему начало той же оси,
|
||||
получаем один наблюдаемый процесс и одну точку успеха: зелёный
|
||||
`world_initialize` означает, что схема создана, стартовый мир принят и его
|
||||
следующий день известен.
|
||||
|
||||
## Что это решение уточняет
|
||||
|
||||
- В [ADR 0007](0007-clickhouse-access.md) DDL больше не принадлежит `etl`:
|
||||
прикладную структуру ведёт отдельный `lifecycle`.
|
||||
- В [ADR 0009](0009-world-control.md) отсутствие `world_position` больше не
|
||||
означает готовый стартовый мир. Начальную позицию ставит
|
||||
`world_initialize`, а работники принимают только `8` и больше.
|
||||
- Одноразовые службы Compose и shell-ожидание, описанные прежним устройством
|
||||
хранилища, отменены. Их последовательность стала наблюдаемым графом Airflow.
|
||||
|
||||
## Отклонено
|
||||
|
||||
- Оставить создание мира в Compose, а позицию исправлять из
|
||||
`make rebuild-storage`: работает технически, но сохраняет двух
|
||||
оркестраторов одного процесса.
|
||||
- Один безусловно разрушительный даг для первого и повторного запуска: обычный
|
||||
`make up` мог бы стереть уже прожитые дни.
|
||||
- Отдельная переменная состояния мира: она дублировала бы
|
||||
`world_position` и могла разойтись с ней. Значения `-1` и `0` выражают
|
||||
нужные переходы в одном месте.
|
||||
- Выдать обычной учётной записи `etl` право удаления баз: рабочий конвейер не
|
||||
должен получать административные права из-за редкой операции восстановления.
|
||||
Reference in new issue
Block a user