Зачем: развилка #134 карты #127 — чем и когда гоняются трансформации DDS и витрины-таблицы, пока спека числила etl_pipeline за этапом 5. Что: дока docs/architecture/etl.md — даг etl_pipeline рождается этапом 4: дёргает dds_transform и dm_transform последовательно (DQ — будущее звено); живой день зовёт его с ожиданием после установки позиции, после разгона пачкой менти запускает рукой, world_initialize — последним шагом; параметров нет, объём выводится из world_position и состояния слоёв; первый шаг — ожидание хвоста hits; механика REPLACE PARTITION — этап 4, этапу 5 — партиционная переобработка. Указатели в доках заказа, сессии, README слоя и ретро-пометки в ADR 0008/0009 переведены на новую доку. Проверка: вычитка; резолюция и доводы — в тикете #134. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
102 lines
8.3 KiB
Markdown
102 lines
8.3 KiB
Markdown
# Дневной конвейер трансформаций
|
||
|
||
Как принятые источники превращаются в модель DDS и витрины DM: кто запускает
|
||
трансформации, когда и в каком объёме. Правила самих сущностей — зерно,
|
||
движки, что именно пересобирается — живут в доках слоя
|
||
([карта модели DDS](dds/README.md)); здесь — только конвейер.
|
||
|
||
Спека обещала `etl_pipeline` этапом 5 — он понадобился раньше. Конвейер
|
||
рождается этапом 4, и временного в нём ничего нет: дневная сборка вперёд
|
||
останется и после. Этапу 5 достаётся его законный предмет — партиционная
|
||
переобработка, умение переиграть произвольный прошлый день по команде.
|
||
|
||
## Форма
|
||
|
||
Дневной конвейер — даг `etl_pipeline`, единственный на стенде. Своей работы
|
||
у него нет: он дёргает слойные даги последовательно и ждёт каждый —
|
||
`dds_transform`, затем `dm_transform`. Контур DQ этапа 4 встанет его же
|
||
звеном после DM; состав звена — предмет развилки витрин (#135) и нарезки.
|
||
|
||
Слойные даги устроены по образцу `orders_ingest`: без расписания, один
|
||
активный прогон, запускаются триггером конвейера или рукой при разборе
|
||
происшествий. Витрины внутри `dm_transform` — отдельные задачи.
|
||
|
||
## Три пути запуска
|
||
|
||
- **Живой день.** `world_live_day` дёргает конвейер с ожиданием — последним
|
||
шагом, после установки позиции. День сыгран — факт, упавшая трансформация
|
||
его не отменяет, поэтому позиция уже стоит; но зеленеть, не узнав,
|
||
разложился ли день, работник не должен ([ADR 0003](../adr/0003-dag-run-state.md)).
|
||
Такой отказ чинится перезапуском `etl_pipeline` рукой — перезапуску ничего
|
||
не нужно помнить, см. «Объём прогона».
|
||
- **Разгон пачкой.** `world_next_day` конвейер не дёргает: пульт управляет
|
||
генератором и только им. После разгона менти запускает конвейер сам —
|
||
и видит, как слои догоняют уехавший вперёд мир.
|
||
- **Рождение мира.** `world_initialize` дёргает конвейер последним шагом:
|
||
`make up` отдаёт стенд с собранными слоями, а не пустые витрины. Уроки
|
||
ручного запуска начинаются со следующих дней.
|
||
|
||
## Объём прогона
|
||
|
||
Параметров у конвейера нет: объём он выводит сам из `world_position` и
|
||
состояния слоёв. Сессии догоняют хвост — от последнего собранного дня до
|
||
последнего прожитого; заказы пересобирают дышащее окно K от позиции; карта
|
||
идентичностей пересчитывается целиком; правила витрин — у витрин (#135).
|
||
Поэтому один и тот же запуск одинаково верен из триггера, с кнопки менти и
|
||
после падения, а разгону на семь дней хватает одного прогона.
|
||
|
||
## Готовность источника
|
||
|
||
Первый шаг `dds_transform` — дождаться, пока хвост топика `hits` дожёван:
|
||
отставание его потребителей нулевое (`system.kafka_consumers`). Зелёный
|
||
приём заказов о событиях не говорит ничего — топики разные и потребители
|
||
разные. В живом режиме зазор ничтожен, но в пакетном генератор выстреливает
|
||
дни в топик быстрее, чем чтец их прожёвывает, и сессии собрались бы по
|
||
недожёванному дню. DQ-сверка такой день подсветит, но починить его до
|
||
этапа 5 нечем: профилактика дешевле ремонта, которого ещё нет.
|
||
|
||
## Идемпотентность
|
||
|
||
Партиция не заменяется простой вставкой. День собирается в таблицу-двойник —
|
||
вставка через Distributed раскладывает строки по шардам, — затем
|
||
`REPLACE PARTITION` на кластере атомарно подменяет кусок каждого шарда. Эта
|
||
механика — часть самих трансформаций и строится этапом 4: без неё дневной
|
||
прогон не повторяем.
|
||
|
||
## Не входит
|
||
|
||
- Партиционная переобработка — переиграть произвольный прошлый день или
|
||
диапазон, включая форму необязательных параметров того же дага, — этап 5
|
||
(#7); туда же стык #111 про дату модельного мира и `logical_date`.
|
||
- Инкрементальный ETL вместо пересборки — техдолг #2.
|
||
- Состав витрин `dm_transform` и звена DQ — #135 и нарезка этапа 4.
|
||
|
||
## Отклонённые варианты
|
||
|
||
- **Задачи трансформаций прямо в работниках пульта.** Один зелёный прогон —
|
||
весь день, но пульт — про ось времени: работников два, шаги дублировались
|
||
бы, а этапу 5 пришлось бы выдирать их обратно.
|
||
- **Make-цель или SQL рукой при приёмке.** Второй оркестратор рядом с
|
||
Airflow — против [ADR 0013](../adr/0013-world-lifecycle.md), — а в живом
|
||
режиме слои копили бы отставание молча.
|
||
- **Временный даг с нейтральным именем до этапа 5.** Дневная сборка вперёд
|
||
никуда не денется, переезд был бы переименованием ради выдуманной границы.
|
||
- **Диапазон дней параметрами триггера.** Ручному запуску менти параметры
|
||
передавать некому, а перезапуск после падения требовал бы помнить conf
|
||
упавшего прогона.
|
||
- **Принять гонку с хвостом `hits`.** Дыра всплыла бы в DQ-сверке, а
|
||
чиниться — не раньше этапа 5.
|
||
|
||
## Связанные решения
|
||
|
||
- [ADR 0003](../adr/0003-dag-run-state.md) — зелёный конец графа не
|
||
переживает отказ выше: отсюда ожидания в триггерах.
|
||
- [ADR 0009](../adr/0009-world-control.md) — пульт мира: работники, которые
|
||
дёргают конвейер живого дня.
|
||
- [ADR 0013](../adr/0013-world-lifecycle.md) — жизненный цикл мира: почему
|
||
прикладным процессом владеет Airflow и почему `world_initialize` кончается
|
||
собранными слоями.
|
||
- Резолюция — тикет #134 карты #127; что пересобирает каждая сущность —
|
||
[заказ](dds/order.md), [сессия](dds/session.md),
|
||
[карта идентичностей](dds/identity.md).
|