Зачем: пробник был одной задачей — в интерфейсе 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>
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_tables — failed,
остальные — 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, и которая сама не такова.