Зависимости задач: оператор сдвига

Урок учит выстраивать порядок выполнения задач в DAG.

Оператор сдвига (битовый сдвиг, два знака «больше») в Airflow задаёт зависимость: a >> b означает «сначала a, потом b».

Стрелки между задачами

Порядок задач в Airflow описывают перегруженными операторами. Запись extract >> transform читается как «extract предшествует transform». Стрелку можно развернуть: transform << extract означает то же самое. Эти стрелки и есть рёбра графа DAG.

a >> b           # a, затем b
a >> b >> c      # цепочка a → b → c
a >> [b, c]      # a, затем b и c параллельно (fan-out)
[b, c] >> d      # d ждёт и b, и c (fan-in)

Параллелизм: fan-out и fan-in

Часто после одного шага можно запустить несколько независимых задач параллельно (fan-out), а потом собрать результат в одной (fan-in). Например, извлекли данные — параллельно проверяем их и считаем статистику, затем грузим.

Параллелизм — не просто красивость, а способ сократить время работы конвейера. Если три независимые проверки занимают по 10 минут каждая, последовательно это 30 минут, а параллельно — всего 10. Airflow сам отслеживает, какие задачи готовы к запуску (все предки завершены), и отдаёт их свободным воркерам. Ваша работа — лишь правильно описать зависимости стрелками, а распараллеливание движок берёт на себя.

Как работает под капотом

DAG с ветвлением. Код требует airflow, помечен text.

from airflow import DAG
from airflow.operators.empty import EmptyOperator
from datetime import datetime

with DAG("branch", start_date=datetime(2026,1,1), schedule="@daily", catchup=False) as dag:
    extract = EmptyOperator(task_id="extract")
    validate = EmptyOperator(task_id="validate")
    stats = EmptyOperator(task_id="stats")
    load = EmptyOperator(task_id="load")

    extract >> [validate, stats] >> load

Проверим логику «задача готова к запуску, когда все её предки завершены» на чистом Python.

done = {"extract", "validate", "stats"}
deps = {"load": ["validate", "stats"], "notify": ["load"]}
for task, need in deps.items():
    ready = all(n in done for n in need)
    print(task, "готов к запуску" if ready else "ждёт предков")

Вывод:

load готов к запуску
notify ждёт предков

Эта проверка «все ли предки завершены» — сердце планировщика Airflow. Каждый тик он проходит по графу, находит задачи, у которых все зависимости успешны, и ставит их в очередь на выполнение. Вам не нужно писать эту логику вручную — достаточно описать зависимости стрелками, а движок сам разберётся с порядком и параллелизмом. Именно поэтому DAG-файл получается декларативным: вы описываете что от чего зависит, а не как именно их запускать по шагам.

Частые ошибки

  • Перепутать направление стрелки. a >> b — это «a до b», а не наоборот; ошибка переворачивает весь порядок.
  • Забыть про fan-in. Если load зависит только от одной из параллельных веток, он запустится раньше готовности второй.
  • Связать задачи из разных DAG напрямую через сдвиг. Оператор сдвига работает внутри одного DAG; между DAG-ами нужны сенсоры или dataset-зависимости.

Итог

  • Оператор сдвига задаёт порядок: a >> b — сначала a, потом b.
  • Список справа даёт fan-out (параллель), список слева — fan-in (сбор).
  • Стрелки — это рёбра графа DAG внутри одного конвейера.
Проверьте себя
1. Как читается запись a >> b в Airflow?
Ab выполняется до a
Ba и b выполняются одновременно
Cсначала a, потом b
Da и b не связаны
2. Что означает запись a >> [b, c]?
Aa зависит от b и c
Bпосле a параллельно запускаются b и c (fan-out)
Cb и c выполняются до a
Dэто синтаксическая ошибка