Files
clickstream-data-platform/docs/adr/0003-dag-run-state.md
T
ddadminandClaude Opus 5 dc12953cac refactor(airflow): пробник ClickHouse разбит на четыре задачи
Зачем: пробник был одной задачей — в интерфейсе Airflow один красный
квадрат, а место отказа приходилось искать по журналу. Ручная машинерия
проброса и сведения ошибок занимала больше места, чем сама проверка, и
читатель продирался через неё раньше, чем понимал, что пробник проверяет.
Пробники — единственный образец DAG в стенде, по ним будут писать
остальные.

Что: test_clickhouse разбит на prepare_tables, write_marker,
read_from_node_2 и cleanup_tables; маркер и имя принявшей запись ноды едут
между задачами через XCom строками. Снято сведение ошибок: except
BaseException, ExceptionGroup, add_note и накопление ошибок в список;
клиент каждая задача заводит общим помощником и закрывает в finally.
Ноды описаны константой NODES парами «имя для человека — источник для
запроса», булев переключатель и параллельные списки подписей ушли.
Уборка идёт обычным правилом запуска, а не all_done: состояние запуска
Airflow считает по концам графа, и уборка, отработавшая после отказа,
покрасила бы в зелёный запуск с упавшей проверкой — решение записано
в ADR 0003. Комментарии остались в четырёх местах: чтение ноды 2 через
remote(), импорт клиента внутри функции, правило запуска уборки и
автосоздание топика в test_kafka. Малые проверки: заглушка task принимает
обе формы декоратора, проверка сведения ошибок заменена проверками
уборки. Красный путь ищет образец по журналам всех задач последнего
запуска, а не в одном самом свежем.

Проверка: make config-test, make smoke (25 проверок) и make smoke-guards
зелены. Разбитый пробник укладывается в 5 секунд из 120, отведённых
run_airflow_probe, — предел не трогаем.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 18:36:52 +03:00

4.3 KiB

ADR 0003. Уборка в DAG и состояние запуска

Дата: 31 июля 2026 года. Статус: принято.

Решение

Задача уборки в DAG стенда идёт обычным правилом запуска: только после успеха всех предыдущих шагов. trigger_rule="all_done" и задачи-teardown в хвосте DAG не используются. Упавший DAG оставляет кластер таким, каким сломался; чистое состояние обеспечивает первая задача следующего запуска — она сносит остатки через DROP ... IF EXISTS и убеждается, что их нет.

Правило шире уборки: в конце DAG не должно стоять задачи, которая зеленеет при отказе предыдущих.

Почему

Состояние запуска Airflow считает по концам графа: зелёные концы — зелёный запуск, а упавшее выше для итога немо. Уборка с all_done написана так, чтобы отработать при любом исходе, и, встав в конец, красит запуск в зелёный именно тогда, когда проверка упала. Для стенда это худший исход: make smoke спрашивает у Airflow одно поле — состояние запуска, — и на сломанном кластере докладывает, что пробник прошёл.

Обычное правило запуска переворачивает счёт по концам в нашу пользу. После отказа выше уборка уходит в upstream_failed, это состояние отказа, и запуск краснеет по ней. Видны обе половины: и отказ рабочей задачи, и отказ самой уборки, если кластер жив, а снести таблицы не вышло. Документированный приём Airflow для тех, кому all_done в хвосте всё же нужен, — отдельная задача-сторож с правилом one_failed; здесь она не понадобилась.

Цена решения — между падением и следующим запуском в default лежат служебные таблицы пробника. Размен принят: тот же выбор уже сделан для постоянного топика Kafka, который пробник не удаляет.

Что проверено

31 июля 2026 года на живом стенде с Airflow 3.3.0. Три прогона test_clickhouse через API: зелёный (четыре задачи success, таблиц после прогона нет), красный с удалённой на ноде 2 локальной таблицей (prepare_tablesfailed, остальные — upstream_failed, состояние запуска failed, следы поломки на кластере), затем снова зелёный (остатки снесены первой задачей, таблиц 0).

Оба отвергнутых варианта проверены так же и оба дали success при упавшей проверке: уборка с trigger_rule="all_done" и уборка as_teardown(on_failure_fail_dagrun=True).

Семантика правил запуска и задач-teardown сверена через MCP Context7 по документации Airflow: раздел про setup/teardown (умолчание on_failure_fail_dagrun=False, правило ALL_DONE_SETUP_SUCCESS) и раздел лучших практик про задачу-сторож. Оттуда же взята формулировка ловушки. Правило прочитано в исходниках установленного Airflow — airflow/models/dagrun.py, is_effective_leaf: концом графа считается задача, ниже которой только задачи-teardown с on_failure_fail_dagrun=False, и которая сама не такова.