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
@@ -6,13 +6,14 @@
|
||||
Генератор играет два источника с разными темпами — поток Метрики и ночную
|
||||
выгрузку магазина; разные темпы дают всё остальное.
|
||||
|
||||
`world-init` играет восемь дней одним прогоном `batch --day 0 --days 8`;
|
||||
слепки отправляет второй запуск — команда `snapshot` того же диапазона (два
|
||||
источника — два запуска, [слепок и его доставка](snapshot.md)), и со сдвигом
|
||||
отправки уезжают **слепки дней 0…6**. Слепок дня 7 снят на границе суток и
|
||||
уедет первым же ходом мира. Особого режима у стартового мира нет: правило
|
||||
отправки живёт в одном месте — в проигрывателе; генератору это решение не
|
||||
стоит ничего.
|
||||
Даг `world_initialize` играет восемь дней одним прогоном
|
||||
`batch --day 0 --days 8`; слепки отправляет второй запуск — команда `snapshot`
|
||||
того же диапазона (два источника — два запуска,
|
||||
[слепок и его доставка](snapshot.md)). Со сдвигом отправки уезжают **слепки
|
||||
дней 0…6**, затем даг `orders_ingest` принимает их в хранилище. Слепок дня 7
|
||||
снят на границе суток и уедет первым же ходом мира. Особого режима у стартового
|
||||
мира нет: правило отправки живёт в одном месте — в проигрывателе; генератору
|
||||
это решение не стоит ничего.
|
||||
|
||||
Что видно снаружи: у последнего прожитого дня клики есть, а заказов нет;
|
||||
глубже — день 0 виден дожившим до конца окна, день 6 — только что родившимся.
|
||||
|
||||
@@ -480,44 +480,35 @@ Airflow читает и собирает эти файлы штатным шаб
|
||||
|
||||
Порядок остальных задают два правила. Первое: матвью принадлежит слою своей
|
||||
цели, а не источника, — разбор из STG в ODS лежит среди файлов ODS, потому что
|
||||
наполняет ODS. Второе: матвью приёма создаётся последней из всех, и потому нарушает
|
||||
нумерацию слоёв. Kafka-движок начинает читать топик ровно тогда, когда к нему
|
||||
наполняет ODS. Второе: матвью приёма создаётся последней из всех, и потому
|
||||
нарушает нумерацию слоёв. Kafka-движок начинает читать топик, когда к нему
|
||||
привязывают первую матвью; создай её раньше разбора — и всё, что доедет в
|
||||
зазоре, ляжет в сырьё и не попадёт в ODS никуда, ни в событие, ни в ошибки. На
|
||||
пустом топике зазор безвреден, поэтому первый прогон о нём не скажет. Проснётся
|
||||
он, когда тома ClickHouse снесены, а данные Kafka целы, — то есть на обычной
|
||||
отладке.
|
||||
|
||||
Применение — двумя одноразовыми сервисами при `make up`, по образцу уже
|
||||
работающих `airflow-init` и `superset-init`. Сначала `kafka-init` создаёт топики
|
||||
— `hits` с двумя партициями и `orders` с одной ([ADR
|
||||
0008](../adr/0008-order-ingestion.md)), — затем `clickhouse-init` дожидается его
|
||||
завершения и применяет файлы с ноды 1: всё `ON CLUSTER`, кроме чтеца заказов —
|
||||
он объявлен только на этой ноде. Порядок страхует `hits` от автосоздания с
|
||||
одной партицией. Переключателей тут два, и путать их не надо: брокер
|
||||
автосоздание разрешает, а потребитель librdkafka внутри ClickHouse его не
|
||||
просит — оба конца измерены, см. «Что проверено». То есть стенд держится на
|
||||
умолчании клиента, а урок «обе ноды читают топик» умирает тихо, поэтому топик и
|
||||
создаётся явно, до применения DDL.
|
||||
Файлы применяет даг `world_initialize` с ноды 1 отдельной учётной записью
|
||||
`lifecycle`: всё `ON CLUSTER`, кроме чтеца заказов — он объявлен только на этой
|
||||
ноде. Перед DDL даг создаёт `hits` с двумя партициями и `orders` с одной
|
||||
([ADR 0008](../adr/0008-order-ingestion.md)). Этот порядок не даёт `hits`
|
||||
автоматически создаться с одной партицией. Брокер автосоздание разрешает, а
|
||||
потребитель librdkafka внутри ClickHouse его не просит. Оба конца измерены;
|
||||
результаты приведены в разделе «Что проверено». Успех дага означает больше,
|
||||
чем применённый DDL: оба стартовых
|
||||
источника приняты, а позиция мира установлена последней задачей. Полный граф и
|
||||
состояния отказа описаны в [спеке жизненного цикла](../specs/2026-08-21-world-lifecycle.md).
|
||||
|
||||
Образцы копируются не целиком, и в двух местах. `clickhouse-init` обязан ждать
|
||||
готовности **обеих** нод: `ON CLUSTER` ждёт исполнения на всех хостах и по
|
||||
таймауту бросает, а `airflow-init` ждёт только первую ноду, `superset-init` —
|
||||
только вторую. И второе: оба образца переживают `make up --wait` лишь потому, что
|
||||
от них зависят долгоживущие сервисы. Что делает `--wait` с одноразовым сервисом
|
||||
без зависимых, проверено при исполнении #37 на Docker Compose 2.40.3: считает
|
||||
его упавшим и возвращает единицу, хотя контейнер вышел с нулём. Поэтому
|
||||
зависимые есть и у новой пары: `clickhouse-init` ждёт `kafka-init`, а
|
||||
`airflow-init` — `clickhouse-init`, и цепочка упирается в долгоживущий Airflow.
|
||||
Побочная выгода важнее обхода `--wait`: к моменту старта Airflow DDL заведомо
|
||||
применён.
|
||||
|
||||
Повторный `make up` поверх живого тома проходит зелёным: весь DDL идёт через
|
||||
`CREATE ... IF NOT EXISTS`. Оборотная сторона — изменённый объект тем же
|
||||
запуском не применяется, причём молча. Отдельного механизма для этого нет и не
|
||||
нужно: правка существующего DDL случается, только пока стенд пишут, а лекарство
|
||||
уже есть — `make clean && make up`. Мир регенерируется, сырьё живёт трое суток,
|
||||
терять нечего.
|
||||
Повторный `make up` поверх готового мира проходит зелёным без изменений:
|
||||
`world_initialize` ветвится раньше DDL. Поэтому изменённый объект обычным
|
||||
подъёмом не применяется. В разработке меняют или пересоздают только затронутый
|
||||
объект, партицию либо базу слоя. Если нужен целиком канонический мир,
|
||||
`make rebuild-storage` запускает `world_recreate`: тот удаляет прикладные базы
|
||||
`stg`, `dm`, `dds`, `ods`, `dic`, позиции групп потребителей и прикладные топики,
|
||||
затем вызывает `world_initialize`. Службы и их тома остаются на месте, а позиция
|
||||
возвращается к концу стартовых дней. Полный `make clean` для изменения DDL не
|
||||
нужен. Когда какая область очистки уместна перед проверкой, задаёт
|
||||
[карта проверок](testing.md).
|
||||
|
||||
## Карта таблиц
|
||||
|
||||
|
||||
@@ -71,9 +71,15 @@
|
||||
тогда окружение окупится, и условие возврата — именно это, а не «стало
|
||||
неудобно».
|
||||
|
||||
Цена самого `make up` — 8 м 18 с с нуля и 3 м 35 с на живом стенде. В цену
|
||||
целей она не входит, но записана здесь по той же причине: подъём пересчитывает
|
||||
оба источника стартового мира и ждёт их приёма.
|
||||
Цена самого `make up` с пустых томов — 8 м 18 с до переезда жизненного цикла в
|
||||
Airflow. В цену целей она не входит. На готовом мире подъём больше не
|
||||
пересчитывает данные: `world_initialize` зеленеет без изменений.
|
||||
|
||||
`make rebuild-storage` тоже не проверка. Команда через Airflow удаляет
|
||||
прикладные базы ClickHouse, топики и позиции групп Kafka, заново применяет DDL,
|
||||
заливает стартовый мир и возвращает его пульт к концу стартовых дней. Службы и
|
||||
их тома она сохраняет, поэтому возврат к каноническим данным не требует
|
||||
холодного запуска всего стенда.
|
||||
|
||||
## Правило: смоук обязан оставаться быстрым
|
||||
|
||||
@@ -96,11 +102,19 @@ ClickHouse отвечает сразу.
|
||||
Деление целей ценно ровно до тех пор, пока оно не превратилось в «гонять
|
||||
всегда всё».
|
||||
|
||||
Состояние перед проверкой восстанавливается в самой узкой достаточной области:
|
||||
сначала объект или партиция, затем база слоя. Если нужен целиком чистый
|
||||
прикладной мир, `make rebuild-storage` пересобирает его базы, топики, позиции
|
||||
потребителей и данные. `make clean` нужен, только когда меняется или сломано
|
||||
постоянное состояние вне этих баз либо проверяется сам запуск с пустых томов.
|
||||
Требование к обоснованию полного сброса задаёт `AGENTS.md`. Неизвестную поломку
|
||||
сначала исследуют: полный сброс уничтожает её следы.
|
||||
|
||||
| Когда | Что гонять |
|
||||
|---|---|
|
||||
| Правка в работе | `make config-test` и `make smoke` |
|
||||
| PR или задача | плюс цели, которых правка касалась: DAG-и, Superset или Kafka — `check-services`; DDL, кластер или данные — `check-clickhouse` |
|
||||
| Приёмка этапа | `make up` с нуля и все три цели на стенде |
|
||||
| Приёмка этапа | все три цели на стенде; запуск с пустых томов — только для изменений начальной сборки или постоянного состояния служб |
|
||||
|
||||
Правки генератора добавляют к этому `make lint`, `make typecheck` и `make test`
|
||||
из каталога `generator/`: стенд им не нужен, а `make test` из них самая
|
||||
@@ -194,12 +208,12 @@ Kafka → STG → ODS, и живёт он в `make check-clickhouse`.
|
||||
| `make check-services` | `scripts/stand-services.sh` |
|
||||
| `make check-clickhouse` | `scripts/check-clickhouse.sh` |
|
||||
|
||||
Особняком — два сценария продолжения `make up`, оба не проверки.
|
||||
`scripts/wait-for-world.sh` ждёт, пока события доедут из Kafka в ODS:
|
||||
`docker compose up --wait` видит конец отправки, но приём идёт асинхронно.
|
||||
Затем `scripts/ingest-starting-orders.sh` разово запускает единственный даг
|
||||
приёма заказов `orders_ingest` и ждёт его конца. Оба ждут ограниченным циклом,
|
||||
а не паузой наугад.
|
||||
Особняком стоит `scripts/run-dag.sh`: это не проверка, а общий REST-переход от
|
||||
`make` к Airflow. `make up` запускает через него `world_initialize`, а
|
||||
`make rebuild-storage` — `world_recreate`; сценарий ждёт появления дага в API,
|
||||
печатает идентификаторы дага и прогона и возвращает вызывающему его итог.
|
||||
Ожидание доставки событий и запуск `orders_ingest` теперь видны задачами
|
||||
`world_initialize`, а не спрятаны в shell.
|
||||
|
||||
Общее у смоука и `check-services` — счёт проверок, обращение к Compose и две
|
||||
проверки — вынесено в `scripts/stand-common.sh`; сам он не запускается.
|
||||
@@ -227,9 +241,38 @@ Kafka → STG → ODS, и живёт он в `make check-clickhouse`.
|
||||
19/2: назвал нездоровый сервис и отсутствие одной из четырёх целей. После
|
||||
возврата сервиса прогон снова дал 21/21.
|
||||
|
||||
**Перезамер 20 августа 2026 года при исполнении #96.** `make up` после
|
||||
`make clean` прошёл за 8 м 18 с, повтор на живом стенде — за 3 м 35 с. Служба
|
||||
семи слепков заняла около 28 секунд между концом `world-init` и своим концом.
|
||||
**Приёмка #119 21 августа 2026 года.** `make rebuild-storage` через Airflow
|
||||
прошёл за 3 м 9 с: позиция прошла задачи `-1` → `0` → `8`, топики получили
|
||||
новые идентификаторы, группы дочитали новые топики без отставания, а ODS принял
|
||||
401 185 событий без брака и 2 851 актуальный заказ. Повторный `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 clean`. Даг создал отсутствующую позицию и закончил на `8`; ODS снова
|
||||
получил 401 185 событий без брака и 2 851 заказ. У `hits` два раздела, у
|
||||
`orders` один, отставание обеих групп равно нулю. На этом стенде прошли смоук
|
||||
20/20, проверка ClickHouse 10/10 и межсервисная проверка 7/7.
|
||||
|
||||
Оборванную очистку проверили после записи `world_position=-1`: остановили
|
||||
первую ноду ClickHouse, и `world_recreate` покраснел на удалении баз. Следующий
|
||||
`make up` тоже покраснел и оставил `-1`. После возврата ноды повторный
|
||||
`make rebuild-storage` восстановил мир.
|
||||
|
||||
**Прототип 21 августа 2026 года при исследовании #110.** Предшествующая
|
||||
shell-версия узкой пересборки за 2 м 25 с пересоздала прикладные базы, отправила
|
||||
401 185 событий и 6 656 версий заказов и вернула позицию с 11 на 8. Замер
|
||||
оставлен как ориентир цены, но не подтверждает нынешнюю реализацию Airflow: тот
|
||||
вариант не очищал Kafka и был отклонён как второй оркестратор мира.
|
||||
|
||||
**Перезамер 20 августа 2026 года при исполнении #96, до переезда в Airflow.**
|
||||
`make up` после `make clean` прошёл за 8 м 18 с, повтор на живом стенде — за
|
||||
3 м 35 с. Отправка семи слепков заняла около 28 секунд.
|
||||
|
||||
Прежний замер ниже — 2 м 50 с и 1 м 5 с до появления приёма заказов и пульта
|
||||
мира. Весь рост приписывать слепкам нельзя: на холодном прогоне основное время
|
||||
ушло ещё до сценариев ожидания: `airflow-init` завершился примерно на 320-й
|
||||
|
||||
Reference in new issue
Block a user