feat(etl): добавлен дневной конвейер трансформаций

Зачем:
- Один запуск должен одинаково работать из жизненного цикла, вручную после разгона и повторно после отказа.

Что:
- Добавлена последовательная сцепка DDS и DM с ожиданием каждого слоя.
- DDS дожидается смещений Kafka и очереди доставки ClickHouse за общий срок.
- ETL встроен в создание мира и живой день; документация приведена к новому графу.

Проверка:
- make config-test; make lint.
- make smoke: 21/21; make check-clickhouse: 10/10; make check-services: 7/7.
- make rebuild-storage, живой и пакетный дни, искусственный отказ и ручное восстановление ETL.
This commit is contained in:
ddadmin committed 2026-08-27 02:43:29 +03:00
1 parent fb00bbe4c3
commit 12a5c109b2
10 files changed
+359 -43

No files matched your search

+2 -2
View File
@@ -125,9 +125,9 @@ Airflow. Партиция — день данных; прогон звена DQ
`TRUNCATE+INSERT` выполняется той же механикой замены, что и у витрин, —
ничего отдельного учить не надо.
## Звено DQ конвейера
## Будущее звено DQ конвейера
Встаёт в `etl_pipeline` после DM ([конвейер](etl.md)) и проверяет только
Встанет в `etl_pipeline` после DM ([конвейер](etl.md)) и проверит только
объекты стенда:
- счётная сверка сессий с эталоном `VisitID` — кука × день, эталон читается
+29 -12
View File
@@ -13,8 +13,8 @@
Дневной конвейер на стенде один — даг `etl_pipeline`. Своей работы
у него нет: он дёргает слойные даги последовательно и ждёт каждый —
`dds_transform`, затем `dm_transform`. Контур DQ этапа 4 встаёт его же
звеном после DM; состав звена — в [доке слоя DM](dm.md).
`dds_transform`, затем `dm_transform`. Будущее звено DQ встанет после DM.
Его состав описан в [доке слоя DM](dm.md).
Слойные даги устроены по образцу `orders_ingest`: без расписания, один
активный прогон, запускаются триггером конвейера или рукой при разборе
@@ -22,6 +22,14 @@
## Три пути запуска
```text
world_initialize ─┐
world_live_day ───┼─> etl_pipeline ─> dds_transform ─> dm_transform
запуск рукой ─────┘
world_next_day ─────> источники без ETL
```
- **Живой день.** `world_live_day` дёргает конвейер с ожиданием — последним
шагом, после установки позиции. Порядок несёт смысл: день сыгран — факт,
упавшая трансформация его не отменяет, поэтому позиция ставится раньше; но
@@ -73,17 +81,26 @@ ODS и ещё не собранный в DDS, затем при необходи
## Готовность источника
Первым шагом `dds_transform` обязан дождаться, пока хвост топика `hits`
дожёван: отставание его потребителей нулевое (`system.kafka_consumers`).
Зелёный приём заказов о событиях не говорит ничего — топики разные и
потребители разные. В живом режиме зазор ничтожен, но в пакетном генератор
выстреливает дни в топик быстрее, чем чтец их прожёвывает, и сессии собираются
по недожёванному дню. DQ-сверка такой день подсветит, но починить его до
этапа 5 нечем: профилактика дешевле ремонта, которого ещё нет.
Первый шаг `dds_transform` проходит два барьера за общий срок в 300 секунд.
Сначала клиент `confluent-kafka` сверяет подтвержденные смещения группы
`clickstream_hits` с верхними смещениями обоих разделов `hits`. Группа, оба
раздела и все смещения должны существовать. Суммарное отставание должно стать
нулевым. Каждый опрос пишет в журнал оба смещения и отставание по разделу.
Шага этого в даге ещё нет — он приезжает отдельным тикетом (#156), и до тех
пор дыра открыта: сессии считают день прожитым по позиции мира, а хвост потока
не стережёт никто. На приёмке отставание смотрят руками.
Только после этого шаг проверяет обе ноды ClickHouse через
`clusterAllReplicas`: у `ods.event_dist` в `system.distribution_queue` не
должно остаться файлов. Каждый опрос пишет в журнал ноды, число файлов и
`last_exception` очереди. Оба барьера опрашиваются раз в три секунды и делят
один крайний срок. После Kafka срок не отсчитывается заново.
Зелёный приём заказов о событиях не говорит ничего — топики разные и
потребители разные. Одного нулевого отставания Kafka тоже мало: чтец мог
подтвердить сообщение раньше, чем Distributed-таблица доставила файл на шард.
Два барьера закрывают обе части пути до того, как сессии выберут свой объем.
Ждущие вызовы сделаны `TriggerDagRunOperator`. Проверка через Context7 по
ближайшей доступной документации Airflow 3.x (версия 3.1.6) подтвердила, что
оператор опрашивает точный созданный им прогон и краснеет вслед за потомком.
## Идемпотентность
+27
View File
@@ -237,6 +237,33 @@ Kafka → STG → ODS, и живёт он в `make check-clickhouse`.
## Что проверено
**Приемка #156 27 августа 2026 года.** `make rebuild-storage` занял 7 минут.
На чистом прикладном мире `world_initialize` сам довел позицию до `8`, дождался
`etl_pipeline` и оставил заполненными DDS и обе витрины. После пересборки прошли
`make smoke` 21/21 и `make check-clickhouse` 10/10.
Два прогона `world_next_day` не изменили слои; один ручной `etl_pipeline` без
параметров догнал оба дня. Полный прогон `world_live_day` занял 27 м 2 с и
завершился только после успешных DDS и DM.
Временная синтаксическая поломка `sql/dm/daily_traffic_rebuild.sql` дала
красную цепочку: DDS завершился успешно, а `daily_traffic`, DM, `etl_pipeline`
и `world_live_day` покраснели. После возврата исправного SQL ручной
`etl_pipeline` без параметров завершился успешно на позиции `12` и восстановил
слои.
Для проверки пакетного барьера `stg.hits_raw_mv` временно отсоединили командой
`DETACH` на обеих нодах. После `world_next_day` отставание Kafka составило
46 367 (19 128 + 27 239). Во время ожидания матвью вернули командой `ATTACH`.
Журнал готовности записал повторные опросы с отставанием 46 367, затем 27 239
и 0; после этого очередь `ods.event_dist` содержала 0 файлов на обеих нодах,
и ETL завершился успешно. Проверка `EXISTS` снова дала `1` на обеих нодах,
позиция равна `13`.
После пересборки `make check-services` дважды дал 5/1: только импорт Superset
не уложился в существующий тайм-аут 30 с. Диагностический пробник импорта с
ожиданием 90 с прошел 3/0. Финальный штатный повтор прошел 7/7 за 129 с.
**Перезамер 21 августа 2026 года при исполнении #124.** Образ Airflow собран с
`apache-airflow-providers-clickhousedb==1.0.0`,
`apache-airflow-providers-common-sql==2.0.1` и