Apache Airflow на собеседовании Data Engineer
Содержание:
Зачем Airflow на собесе DE
Airflow — стандарт оркестрации в большинстве российских IT-компаний. На собесе Data Engineer базовые знания Airflow ожидают по умолчанию: что такое DAG, как ставится зависимость между тасками, что такое идемпотентность.
Глубокие вопросы — для middle+:
- Как работает scheduler и зачем catchup
- В чём разница между PythonOperator и BashOperator при запуске одного и того же скрипта
- Когда использовать sensor, а когда — пуллить из downstream
- Как проектировать DAG, чтобы он переживал перезапуск с любого таска
DAG, операторы, зависимости
DAG (Directed Acyclic Graph) — направленный ациклический граф тасков. Главные элементы:
- Operator — что делает таск (PythonOperator, BashOperator, KubernetesPodOperator и т.д.)
- Task — экземпляр оператора в DAG
- Dependencies — порядок выполнения, через
>>илиset_upstream/downstream
Минимальный DAG:
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
with DAG(
'simple_dag',
start_date=datetime(2026, 1, 1),
schedule='@daily',
catchup=False,
) as dag:
extract = PythonOperator(task_id='extract', python_callable=extract_fn)
transform = PythonOperator(task_id='transform', python_callable=transform_fn)
load = PythonOperator(task_id='load', python_callable=load_fn)
extract >> transform >> loadЧто важно знать на собесе:
schedule— cron-выражение или alias (@daily,@hourly)catchup— если True, при первом запуске Airflow догонит все execution dates сstart_datemax_active_runs— сколько одновременных run-ов разрешено для DAGtask_concurrency— сколько одновременных запусков одного таска
Идемпотентность и backfill
Идемпотентность — таск можно перезапустить с теми же параметрами и получить тот же результат. Без идемпотентности backfill не работает.
Пример неидемпотентного таска:
INSERT INTO events_agg (date, count)
VALUES ('2026-05-01', 10000);При повторном запуске — дубликат. Идемпотентный вариант:
DELETE FROM events_agg WHERE DATE = '{{ ds }}';
INSERT INTO events_agg (DATE, count) ... ;Или upsert с ON CONFLICT DO UPDATE. Или партиционированная таблица с INSERT OVERWRITE PARTITION.
Backfill — запуск DAG для прошлых execution dates. Используется, когда:
- Нужно перегенерить данные после фикса бага
- Догнать дни, когда DAG был выключен
- Заполнить данные за период до запуска DAG
Команда:
airflow dags backfill -s 2026-01-01 -e 2026-01-31 my_dagНа собесе спросят: «Что произойдёт, если запустить backfill на DAG с неидемпотентными тасками?» Ответ: дубликаты, рассинхронизация, длительный дебаг.
XCom и шаблоны Jinja
XCom — механизм передачи данных между тасками. Ограничение — в дефолтном backend (БД) хранится только маленький JSON, для больших объёмов — кастомный backend.
def push_value(**context):
context['ti'].xcom_push(key='user_count', value=12345)
def pull_value(**context):
count = context['ti'].xcom_pull(key='user_count')Jinja-шаблоны — встроены в большинство операторов. Главные переменные:
{{ ds }}— execution date в формате YYYY-MM-DD{{ ds_nodash }}— то же без дефисов (для путей){{ ts }}— execution timestamp ISO{{ macros.ds_add(ds, -7) }}— дата за 7 дней до execution
Используются в SQL-запросах, путях файлов, аргументах bash:
load = BashOperator(
task_id='load',
bash_command='spark-submit /jobs/etl.py --date={{ ds }}',
)Sensor-операторы
Sensor — ждёт условия, потом завершается. Используется когда зависимость от внешней системы (файл появился, таблица заполнена).
from airflow.sensors.filesystem import FileSensor
wait_file = FileSensor(
task_id='wait_for_input',
filepath='/data/input/{{ ds }}.csv',
poke_interval=60,
timeout=3600,
mode='reschedule',
)Главный антипаттерн — mode='poke' (по дефолту). Sensor занимает worker slot всё время ожидания. На длинных таймаутах это убивает кластер. Используйте mode='reschedule' — sensor освобождает slot между проверками.
Типичные задачи на собесе
1. Спроектировать DAG для ETL
Условие: «Каждый день забираем данные с API, трансформируем, кладём в DWH». Ответ должен включать:
- Структуру: extract → transform → load
- Идемпотентность: как работает повторный запуск
- Обработку ошибок: retries, on_failure_callback
- Алерты: SLA или callback в Slack
2. Дебаг сломанного DAG
Дают DAG-код с проблемой. Типичные баги: cyclic dependency, неправильный schedule, неидемпотентный таск, race condition между параллельными ветками.
3. Оптимизация медленного DAG
«У нас DAG из 50 тасков выполняется 4 часа. Как ускорить?» Ответ:
- Параллелить независимые ветки
- Группировать мелкие таски в один (TaskGroup)
- Убрать неоправданные sensor-ы
- Использовать KubernetesPodOperator для тяжёлых тасков
- Профилировать конкретный таск
4. Архитектура мониторинга
Какие метрики важны: SLA по DAG, время выполнения каждого таска, количество ошибок, задержка scheduler. Где собирать (StatsD, Prometheus).
Частые ошибки
Бизнес-логика в DAG-файле. DAG-файл парсится scheduler каждые 30 секунд. Если в нём тяжёлый код (например, обращения к БД), это ломает scheduler.
Глобальные переменные в DAG. Между парсингами scheduler состояние не сохраняется. Используйте Airflow Variables или Connections, а не модульные переменные.
Sensor в poke-режиме на длинных таймаутах. Worker slot заблокирован, кластер деградирует.
XCom для больших данных. XCom хранится в БД метаданных. Для больших объёмов используйте S3/HDFS + только путь в XCom.
Не использовать catchup=False. При запуске DAG со start_date в прошлом по умолчанию запустит все пропущенные дни. Если этого не хотите — обязательно catchup=False.
FAQ
Какую версию Airflow учить?
Airflow 2.x. Версия 1.x устарела. На собесе спросят, что нового в 2.x: TaskFlow API, dynamic task mapping, dataset-driven scheduling.
Airflow vs Dagster vs Prefect — что выбрать?
Airflow — стандарт в РФ. Dagster и Prefect — современные альтернативы, иногда встречаются в стартапах. Для собеса — учите Airflow.
Сколько практики Airflow нужно для junior DE?
Один-два домашних проекта с боевым DAG (с sensor, XCom, идемпотентностью). На собесе спросят про реальный опыт.
Это официальная информация?
Нет. Статья основана на публичных источниках и опыте кандидатов.