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>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# 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`, и которая сама не такова.
|
||||
Reference in New Issue
Block a user