Files
clickstream-data-platform/docs/architecture/etl.md
T
ddadminandClaude Fable 5 ebe3de4ae8 docs(etl): спроектирован дневной конвейер трансформаций
Зачем: развилка #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>
2026-08-25 03:22:35 -04:00

8.3 KiB

Дневной конвейер трансформаций

Как принятые источники превращаются в модель DDS и витрины DM: кто запускает трансформации, когда и в каком объёме. Правила самих сущностей — зерно, движки, что именно пересобирается — живут в доках слоя (карта модели DDS); здесь — только конвейер.

Спека обещала etl_pipeline этапом 5 — он понадобился раньше. Конвейер рождается этапом 4, и временного в нём ничего нет: дневная сборка вперёд останется и после. Этапу 5 достаётся его законный предмет — партиционная переобработка, умение переиграть произвольный прошлый день по команде.

Форма

Дневной конвейер — даг etl_pipeline, единственный на стенде. Своей работы у него нет: он дёргает слойные даги последовательно и ждёт каждый — dds_transform, затем dm_transform. Контур DQ этапа 4 встанет его же звеном после DM; состав звена — предмет развилки витрин (#135) и нарезки.

Слойные даги устроены по образцу orders_ingest: без расписания, один активный прогон, запускаются триггером конвейера или рукой при разборе происшествий. Витрины внутри dm_transform — отдельные задачи.

Три пути запуска

  • Живой день. world_live_day дёргает конвейер с ожиданием — последним шагом, после установки позиции. День сыгран — факт, упавшая трансформация его не отменяет, поэтому позиция уже стоит; но зеленеть, не узнав, разложился ли день, работник не должен (ADR 0003). Такой отказ чинится перезапуском 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, — а в живом режиме слои копили бы отставание молча.
  • Временный даг с нейтральным именем до этапа 5. Дневная сборка вперёд никуда не денется, переезд был бы переименованием ради выдуманной границы.
  • Диапазон дней параметрами триггера. Ручному запуску менти параметры передавать некому, а перезапуск после падения требовал бы помнить conf упавшего прогона.
  • Принять гонку с хвостом hits. Дыра всплыла бы в DQ-сверке, а чиниться — не раньше этапа 5.

Связанные решения

  • ADR 0003 — зелёный конец графа не переживает отказ выше: отсюда ожидания в триггерах.
  • ADR 0009 — пульт мира: работники, которые дёргают конвейер живого дня.
  • ADR 0013 — жизненный цикл мира: почему прикладным процессом владеет Airflow и почему world_initialize кончается собранными слоями.
  • Резолюция — тикет #134 карты #127; что пересобирает каждая сущность — заказ, сессия, карта идентичностей.