Backfill: перезаливка истории
Урок объясняет backfill — запуск конвейера задним числом за прошлые периоды.
Backfill — выполнение DAG за прошедшие даты: например, чтобы пересчитать отчёты за весь прошлый месяц после исправления логики.
Зачем гонять прошлое
Представьте: вы нашли баг в преобразовании, который две недели портил витрину. Исправили код — но старые данные уже неверны. Backfill позволяет прогнать обновлённый конвейер за каждый из прошедших дней и пересчитать всё корректно. Это одна из главных причин, почему конвейеры строят на оркестраторе, а не на голом cron.
Backfill нужен не только при багах. Типичные ситуации: запустили новый отчёт и хотите видеть в нём историю за полгода назад; добавили новую колонку в витрину и нужно заполнить её для старых дней; источник прислал исправленные данные за прошлую неделю. Во всех случаях задача одна — прогнать конвейер за прошлые периоды так, будто он работал тогда. И ровно поэтому в задачах используется логическая дата, а не now(): только так backfill за июнь возьмёт июньские данные.
было неверно: [01][02][03][04] ... [14] (баг в transform)
backfill: ▶ прогнать DAG заново за 01..14 с исправленной логикой
стало верно: [01][02][03][04] ... [14] ✔Backfill против catchup
| Backfill | Catchup |
| запускают вручную за диапазон дат | авто-догон пропусков от start_date |
| осознанное действие инженера | поведение при включении DAG |
Команда backfill в CLI задаёт диапазон дат. Это bash, не исполняется в браузере.
airflow dags backfill daily_report \
--start-date 2026-06-01 \
--end-date 2026-06-14Как работает под капотом
Главное условие успешного backfill — идемпотентность: повторный прогон за дату должен заменять данные, а не добавлять дубли. Смоделируем «перезапись партиции за день» — backfill за 02 июня просто переписывает данные этого дня.
warehouse = {"2026-06-01": 100, "2026-06-02": 50} # уже загружено
def load_day(day, value):
warehouse[day] = value # перезапись, не += → идемпотентно
# backfill за 02 июня с исправленным значением
load_day("2026-06-02", 999)
for day in sorted(warehouse):
print(day, warehouse[day])Вывод:
2026-06-01 100 2026-06-02 999
Обратите внимание: backfill за 02 июня не затронул 01 июня — каждый день обрабатывается независимо. Это и есть сила «партиция-на-день»: можно перезалить любой проблемный период, не трогая соседние и не боясь задеть корректные данные. На практике инженеры дробят большой backfill на части (например, по неделям) и запускают с паузами, чтобы не положить источник и хранилище лавиной исторических запросов.
Частые ошибки
- Backfill неидемпотентного конвейера. Если загрузка добавляет строки (
INSERTбез перезаписи), повторный прогон удвоит данные за день. - Brать в коде
now()вместо логической даты. Тогда backfill за июнь обработает июньские данные текущими, а не историческими. - Запускать гигантский backfill без пауз. Десятки тысяч исторических запусков разом перегрузят источники; диапазон дробят.
Итог
- Backfill прогоняет конвейер за прошедшие даты, например после исправления логики.
- Он отличается от catchup: backfill запускают вручную за диапазон дат.
- Безопасный backfill требует идемпотентности — перезаписи, а не добавления данных.