Errors
Apache Airflow

Apache Airflow: How to prevent airflow from backfilling dag runs?

Ты поставил новый даг на почасовое выполнение, а он вдруг запускается двадцать раз подряд, забивая очередь и съедая ресурсы. Знакомо? Это backfilling, и он може

Ты поставил новый даг на почасовое выполнение, а он вдруг запускается двадцать раз подряд, забивая очередь и съедая ресурсы. Знакомо? Это backfilling, и он может быть совершенно бесполезным, а то и вредным.

Например, твой даг забирает данные из API, который обновляется раз в час. Каждая последующая попытка за прошлый час будет тянуть одно и то же. А Airflow по умолчанию честно попытается нагнать все пропущенные интервалы с момента start_date.

Почему это происходит

Всё дело в дефолтном поведении Airflow и в параметре catchup. Если даг создан с start_date в прошлом и расписанием (schedule), scheduler увидит пропущенные интервалы и создаст на каждый из них дагран.

Вот типичный сценарий:

from datetime import datetime
from airflow import DAG

default_args = {
    'start_date': datetime(2024, 1, 1, 0, 0),  # Давно в прошлом
}

dag = DAG(
    'my_hourly_dag',
    default_args=default_args,
    schedule_interval='@hourly',
    # catchup не указан - используется глобальная настройка
)

Если такой даг развернуть 1 февраля 2024 года, scheduler попытается выполнить его за все 744 пропущенных часа января. И сделает это быстро, один за другим.

Как остановить безумие

Решение прямое — явно отключить догоняние для дага. Для этого используется параметр catchup=False.

dag = DAG(
    'my_smart_dag',
    default_args=default_args,
    schedule_interval='@hourly',
    catchup=False,  # Ключевая строка
)

С этой настройкой scheduler создаст только один дагран — для следующего интервала после момента развёртывания. Всё прошлое останется в покое.

Настройка для всех дагов

Менять каждый даг вручную неудобно. Можно задать поведение по умолчанию для всего Airflow. Начиная с версии 1.8, в конфигурации есть параметр catchup_by_default.

# airflow.cfg
[scheduler]
catchup_by_default = False

Или через переменную окружения: AIRFLOW__SCHEDULER__CATCHUP_BY_DEFAULT=False. После этого все новые даги по умолчанию не будут догонять прошлое. Для старых дагов, где catchup явно не задан, применится новое значение.

А что с динамическим start_date?

В FAQ советуют не использовать datetime.now() как start_date. Это мудрый совет. Если задать start_date=datetime.now() и catchup=False, то первый запуск может произойти только через целый интервал (час, день), что неочевидно.

Лучше всего — фиксированная дата в прошлом плюс явное catchup=False. Или использовать последнее выполнение как точку отсчёта для логики внутри тасок, если данные инкрементальные.

Итог

Backfilling — это фича, а не баг, но она нужна не всегда. Чтобы её отключить, выставляй catchup=False в аргументах DAG. Для глобального изменения переключи catchup_by_default в конфиге. И не используй динамический start_date, чтобы не запутать ни scheduler, ни себя.