Backfill
Backfill
Backfill (обратное заполнение) — приём, при котором систему заставляют задним числом посчитать или собрать данные за прошлые периоды: те, которые она пропустила, никогда не считала или считала по устаревшим правилам. Ключевая идея — прогнать ту же самую логику, что работает каждый день, но по историческим датам, и сделать это так, чтобы повторный запуск не портил уже собранное.
История
У этого термина нет автора, года и патента — и это честный ответ. Слово пришло из строительства: backfill по-английски — «обратная засыпка», грунт, которым закапывают траншею после того, как уложили трубу. Инженеры по данным просто утащили метафору: яма в истории есть, её надо засыпать.
Но есть вполне конкретная линия, по которой понятие превращалось из бытового слова в инженерную практику с инструментами.
- Конец 1980-х — 1990-е, США. Хранилища данных. Билл Инмон (Bill Inmon) и Ральф Кимбалл (Ralph Kimball) формулируют идею data warehouse — отдельной базы, куда из операционных систем регулярно перегружают данные. Кимбалл описывает это в «The Data Warehouse Toolkit» (первое издание — 1996). Именно там появляется рутина «а теперь загрузим историю за три года назад», потому что витрина без истории бесполезна: сравнивать не с чем.
- ~2006–2012. Эпоха Hadoop. Появляется дешёвое место, где можно хранить сырые логи целиком и пересчитывать их сколько угодно раз. Пересчёт истории перестаёт быть героизмом и становится обычной операцией.
- Около 2011–2012. Лямбда-архитектура. Натан Марц (Nathan Marz), автор Apache Storm, предлагает схему из двух слоёв: batch layer (пакетный, медленный, считает всё с нуля по полному архиву) и speed layer (быстрый, считает последние минуты). Книга «Big Data» Марца и Джеймса Уоррена вышла в 2015-м. Здесь бэкфилл впервые становится архитектурным принципом, а не аварийной процедурой: пакетный слой по сути делает бэкфилл постоянно, и именно этим лечит любые ошибки быстрого слоя.
- Октябрь 2014, Airbnb. Apache Airflow. Максим Бошман (Maxime Beauchemin) пишет планировщик пайплайнов, в котором бэкфилл встроен как первоклассная операция: команда
airflow backfillи параметрcatchup, который при включении заставляет планировщик догнать все пропущенные интервалы с даты старта. Airflow открыт в 2015-м, в инкубаторе Apache — с 2016-го, проект верхнего уровня — с января 2019-го. Именно Airflow закрепил в отрасли словарь: DAG, интервал, execution date, бэкфилл. - 2014. Каппа-архитектура. Джей Крепс (Jay Kreps), один из авторов Apache Kafka, публикует заметку «Questioning the Lambda Architecture» и предлагает убрать пакетный слой вовсе: если весь поток событий лежит в логе, то бэкфилл — это просто перечитать лог с нужной позиции новым кодом. Бэкфилл превращается из отдельной процедуры в «перемотку назад».
- 2016 и далее. dbt. Инструмент от Fishtown Analytics (сейчас dbt Labs) делает массовой практику инкрементальных моделей с ключом
--full-refresh: обычный прогон считает только новое, а с флагом — пересобирает таблицу с нуля. Это бэкфилл, упакованный в одну опцию командной строки.
Текущий статус: это не продукт и не стандарт, а устойчивая практика. Никто им не владеет. Зато почти в каждом серьёзном оркестраторе пайплайнов (Airflow, Dagster, Prefect, Netflix Maestro) слово «backfill» есть прямо в документации и в интерфейсе.
Что это такое
Представь любую систему, которая работает «по дням». Каждую ночь в три часа она что-то делает за вчера: выгружает данные из рекламного кабинета, считает выручку, собирает цены конкурентов, обновляет витрину отчётов. Внутри — код, который принимает на вход одну дату и кладёт результат в хранилище.
Бэкфилл — это когда ты берёшь ровно этот же код и запускаешь его не за вчера, а за 1 июня, 2 июня, 3 июня и так далее до конца августа. Девяносто два раза подряд.
Звучит тривиально, но за этой простотой прячется вся сложность. Ежедневный прогон делает одну попытку, занимает десять минут и в случае сбоя просто повторится завтра. Бэкфилл делает девяносто две попытки, занимает сутки и в случае сбоя на шестьдесят первом дне не должен начинать сначала. Ежедневный прогон один в системе. Бэкфилл идёт параллельно с ежедневным прогоном — и они дерутся за одни и те же ресурсы: за базу, за сетевой канал, за квоту API, за одну и ту же строку в таблице.
Поэтому бэкфилл — это не «запустить скрипт с другой датой». Это отдельный режим работы системы со своими требованиями.
Полезно развести похожие вещи:
- Backfill vs миграция. Миграция меняет форму данных (добавили колонку, поменяли тип, переехали в другую базу). Бэкфилл наполняет данные содержанием. Часто идут парой: сначала миграция добавляет пустую колонку, потом бэкфилл заполняет её для десяти миллионов старых строк.
- Backfill vs репроцессинг (reprocessing). Бэкфилл обычно про дырку: данных не было, надо добыть. Репроцессинг — про пересчёт уже имеющегося новым кодом, потому что старый считал неправильно. На практике граница размыта, и механика одна и та же.
- Backfill vs восстановление из бэкапа. Бэкап возвращает то, что было. Бэкфилл производит то, чего никогда не было, — заново вычисляет историю по сегодняшним правилам.
- Backfill vs catch-up. Catch-up (догон) — частный случай: система стояла три дня, надо догнать три пропущенных интервала. Бэкфилл шире: он может идти на годы назад, в том числе туда, где системы ещё не существовало.
Бэкфилл почти никогда не восстанавливает прошлое — он выдумывает его заново
Ты считаешь историю сегодняшним кодом, по сегодняшним справочникам, из сегодняшнего состояния источника. Если за лето поменялась модель атрибуции, курс валюты, состав справочника регионов или сам источник переписал свои цифры — бэкфилл выдаст числа, которых в июне никто не видел. Это не баг, это природа приёма. Но если ты потом сравниваешь «июнь из бэкфилла» с «июлем из ежедневного прогона» — сравниваешь две разные вселенные.
Аналогии из жизни
Стройка: обратная засыпка траншеи. Буквальный источник термина. Трубу уложили, теперь траншею надо закопать — но не как попало, а слоями, с трамбовкой каждого слоя, иначе через год асфальт просядет. Аналогия хороша тем, что передаёт главное: важна не скорость засыпки, а порядок и контроль качества каждого слоя. Ровно как бэкфилл идёт батчами с проверкой каждого.
Где ломается: грунт инертен — засыпал и забыл, он не меняется. Данные же продолжают приходить, пока ты их засыпаешь. Представь, что пока ты закапываешь траншею с одного конца, с другого её продолжают копать, — вот это ближе к правде. Плюс грунт нельзя «засыпать дважды», а бэкфилл обязан выдерживать повторный прогон.
Библиотека: подшивка пропущенных номеров журнала. Библиотека выписывает журнал с 2020 года, а читателям нужен полный архив с 2015-го. Библиотекарь ищет старые выпуски у букинистов, вклеивает в каталог, проставляет инвентарные номера. Медленно, по одному номеру, иногда номер не находится совсем. Аналогия точно передаёт частичность: бэкфилл редко закрывает 100% дырок, часть истории просто утрачена у источника.
Где ломается: старый номер журнала — физический объект, он идентичен тому, что был в 2015-м. А данные, которые ты добираешь из API сегодня, — это сегодняшняя версия правды о прошлом. Рекламный кабинет мог пересчитать статистику за июнь в июле. Магазин мог удалить половину объявлений. Библиотека не сталкивается с тем, что старый номер журнала по дороге переписали.
Бухгалтерия: проводки задним числом. Нашли неучтённые документы за второй квартал — делаешь корректирующие проводки, пересчитываешь итоги, перезакрываешь период. Отличная аналогия для дисциплины: бухгалтерия не позволяет просто «дописать в прошлое», она требует явной корректировки, следа и переформирования отчёта. Зрелый бэкфилл ведёт себя так же: пишет, что именно и когда было дозаполнено.
Где ломается: в бухгалтерии закрытый период юридически заморожен, и правки — событие. В данных же бэкфилл — рутина, иногда еженедельная. И ещё: бухгалтерия работает с десятками документов, бэкфилл — с миллионами строк, где ручная сверка невозможна в принципе.
Как это работает
Механика зрелого бэкфилла складывается из семи вещей. Пропустишь любую — и он либо развалится на середине, либо тихо испортит данные.
1. Разбиение на единицы. Диапазон режется на атомарные куски — обычно день, иногда час или месяц. Единица должна быть такой, чтобы её обработка помещалась в разумное время (минуты, не часы) и чтобы её можно было повторить целиком.
2. Идемпотентность (повторяемость без побочного эффекта). Главное требование. Прогон за 3 июня, выполненный дважды, должен дать ровно тот же результат, что и один раз. На практике это означает: не INSERT, а UPSERT по ключу (вставить или обновить, если такая строка уже есть), либо «удалить всё за этот день и записать заново». Если этого нет — при первой же ошибке и повторе ты получишь дубли, и заметишь их через месяц по кривому отчёту.
3. Чекпойнты (контрольные отметки). После успешной обработки дня система пишет отметку: «3 июня закрыто». Отметка живёт отдельно от самих данных — в таблице, в файле, в ключе Redis. При перезапуске бэкфилл читает отметки и пропускает закрытое. Без чекпойнтов каждое падение возвращает тебя в начало.
4. Порядок обхода. Неочевидное, но важное решение. От старых к новым — привычно и логично. От новых к старым — часто практичнее: свежие данные нужнее, источник хранит их надёжнее, и если бэкфилл придётся прервать на середине, у тебя закрыт самый ценный кусок. Отдельный случай — обход помесячно: закрыл август целиком, только потом взялся за июль. Так проще отвечать на вопрос «что у нас уже есть».
5. Троттлинг и окно. Источник почти всегда ограничивает частоту запросов (rate limit). Бэкфилл, разогнавшийся на всю мощь, получит бан или уронит чужой сервис. Поэтому — пауза между единицами, ограничение параллельности и временное окно: «работать только с 22:00 до 8:00, днём не мешать».
6. Изоляция от ежедневного прогона. Бэкфилл и обычный ночной запуск не должны идти одновременно, если пишут в одно место или используют один дефицитный ресурс (одно подключение к VPN, один браузер, одну квоту). Классическое решение — взаимное исключение (mutual exclusion): один общий замок, который берёт тот, кто пришёл первым. На Linux это обычно утилита flock, но она не универсальна; переносимый вариант — создать каталог-замок (mkdir атомарен: либо создался, либо нет, третьего не дано), положить туда свой PID и проверять, жив ли ещё прежний владелец.
7. Ретраи и верификация. Ошибка на одном дне не должна ронять весь процесс: пауза, повтор, ограниченное число попыток, потом — пометить день как проблемный и идти дальше. В конце — сверка: все ли дни диапазона закрыты, нет ли аномалий в объёме (день с нулём строк среди дней по десять тысяч — почти наверняка ложный успех).
Псевдокод, который собирает всё вместе:
взять общий замок; если занят — выйти (мы не одни)
дни = список дат от начала до конца, в выбранном порядке
для каждого дня:
если есть чекпойнт "день закрыт" — пропустить
если вышли за временное окно — ждать до следующего окна
попытка = 0
пока попытка < максимума:
результат = обработать(день) # та же логика, что в ежедневном прогоне
если результат == успех:
записать данные через upsert # идемпотентно
поставить чекпойнт(день)
прервать цикл попыток
иначе:
попытка += 1
подождать(пауза * попытка) # экспоненциальная выдержка
сверить: все дни закрыты? объёмы правдоподобны?
снять замок; уведомить человека с итогом
Самая дорогая ошибка бэкфилла — не падение, а тихий ложный успех
Источник вернул пустой ответ вместо ошибки, скрипт записал ноль строк, поставил чекпойнт и радостно пошёл дальше. Формально всё зелёное: девяносто два дня закрыты. Фактически — сорок из них пустые, и узнаешь ты об этом через месяц, когда кто-то откроет отчёт. Поэтому проверять надо не только код возврата, но и правдоподобность объёма: «в этот день данных подозрительно мало» — это повод для повтора, а не для чекпойнта.
Где встречается в обычной жизни
- Подключил новый банк в приложении-агрегаторе — и через полчаса там появляется история операций за два года. Это бэкфилл: приложение постранично вытянуло выписку из банковского API и разложило по категориям своими сегодняшними правилами. Поэтому категории у старых трат иногда выглядят странно — их размечал алгоритм 2026 года, а не тот, что работал в 2024-м.
- Фитнес-браслет неделю лежал без телефона, а потом синхронизировался — и в приложении задним числом появились шаги и сон за все семь дней. Часы буферизовали сырые события, а приложение пересчитало из них метрики.
- Цифры во вчерашнем отчёте аналитики через день изменились. Не мистика: часть событий доезжает с опозданием (late-arriving data), и система периодически перезакрывает недавние дни. Это регулярный бэкфилл скользящего окна.
- Подписался на подкаст — и в ленте появились все 300 выпусков, включая те, что вышли до твоей подписки. Клиент дочитал RSS-ленту до начала.
- Зашёл в мессенджер с нового устройства, и переписка подгружается кусками, от свежих сообщений к старым. Классический порядок «от новых к старым»: важное — сразу, остальное — фоном.
Где встречается в IT и бизнесе
- Новая метрика в аналитике. Придумали считать «долю повторных обращений». Код готов, но менеджеру нужен график с января, а не с сегодняшнего дня. Без бэкфилла метрика бесполезна год.
- Нашли баг в расчёте. Полгода считали выручку без учёта возвратов. Исправили формулу — и обязаны пересчитать всю историю, иначе в отчётах будет ступенька на дате деплоя, и все выводы за период окажутся несопоставимы.
- Миграция схемы базы данных. Добавили колонку
client_segmentк таблице заказов. Новые заказы её получают сразу, а десять миллионов старых — только через фоновый бэкфилл батчами по несколько тысяч строк, с паузами, чтобы не положить боевую базу. - Подключение нового источника. Завели интеграцию с рекламным кабинетом, CRM или маркетплейсом. Ежедневная выгрузка заработала с сегодня, а руководителю нужно сравнение год к году — значит, отдельной процедурой вытягиваем историю за нужный период.
- Обучение ML-моделей. Модели нужны признаки (features) на исторических данных — те же, что будут в проде. Пересчёт признаков по архиву — это бэкфилл, и именно на нём чаще всего ловят утечку данных из будущего (когда в признак за июнь случайно попадает информация из июля).
Кто пользуется
Практически все, у кого есть аналитика и пайплайны данных.
- Airbnb — родина Airflow. Бэкфилл там встроенная операция планировщика, а не самодельный скрипт.
- Netflix — собственные оркестраторы (Meson, затем Maestro, открытый в 2024-м). По публичным заявлениям компании, счёт идёт на десятки тысяч workflow и сотни тысяч запусков в день; перезапуск исторических интервалов — штатная кнопка.
- Uber, Spotify, LinkedIn — все публично описывали свои платформы данных, и во всех бэкфилл вынесен в отдельный контур с приоритетом ниже, чем у ежедневных задач, чтобы не выедать кластер.
- Банки и телеком — регуляторная отчётность регулярно требует пересчёта закрытых периодов по изменившейся методике. Там бэкфилл — это процедура с регламентом, актом и подписью.
- Любой маркетинг с атрибуцией. Рекламные системы сами корректируют статистику задним числом (конверсии доезжают неделями), поэтому грамотная выгрузка всегда перезакрывает последние 7–30 дней. То есть работает в режиме непрерывного мини-бэкфилла.
Альтернативы и конкуренты
Полный пересчёт (full refresh). Не мучиться с диапазонами, а раз в сутки собирать таблицу с нуля.
Плюсы: никаких чекпойнтов, дублей и хитрой логики — результат всегда консистентен, а код ровно один.
Минусы: линейно дорожает с объёмом; на терабайтах превращается в многочасовую задачу и большой счёт за вычисления.
Каппа-подход: реплей из лога событий. Хранить все сырые события в Kafka или подобном, а любой пересчёт делать перемоткой лога новым кодом.
Плюсы: одна кодовая база для потока и для истории; реплей воспроизводим с точностью до события.
Минусы: требует хранить лог настолько глубоко, насколько нужна история, — а это дорого; не спасает, если данных в логе никогда не было (источник подключили позже).
Ленивый бэкфилл (on-demand). Не заполнять заранее, а вычислять при первом обращении и кешировать.
Плюсы: платишь только за то, что реально спросили; старт мгновенный.
Минусы: первый пользователь ждёт; невозможно построить график за год одним запросом; непредсказуемая нагрузка.
Отказ с честной отметкой. Не заполнять вовсе, а явно показывать «данные доступны с 19 сентября 2026».
Плюсы: ноль работы, ноль риска испортить историю, ноль ложной точности.
Минусы: нет сравнения год к году; и почти всегда через месяц кто-то всё равно попросит историю.
Когда НЕ стоит использовать
- Когда источник больше не отдаёт достоверное прошлое. Если рекламный кабинет хранит детализацию только 90 дней, а дальше отдаёт агрегаты по другой методике, бэкфилл за год даст цифры, которые нельзя стыковать со свежими. Лучше явный разрыв в данных, чем незаметно несопоставимые числа: дырку видно, а кривые цифры выглядят как настоящие.
- Когда историей никто не будет пользоваться. Бэкфилл за три года ради метрики, на которую посмотрят один раз, — это недели вычислений и риск задеть боевые данные ради удовлетворённого любопытства. Спроси, какое решение изменится от наличия истории; если никакое — не делай.
- Когда пайплайн ещё не идемпотентен. Запускать бэкфилл поверх кода, который умеет только дописывать строки, — гарантированные дубли. Сначала upsert и чекпойнты, потом история. Порядок обратный всегда заканчивается ручной чисткой таблицы.
- Когда нет изоляции от боевой нагрузки. Бэкфилл — это управляемый DDoS собственной инфраструктуры. Без ограничения скорости, отдельного пула соединений и окна он уронит то, что работало нормально.
Связанные понятия
- Идемпотентность — свойство операции давать тот же результат при повторном выполнении; фундамент любого бэкфилла.
- Чекпойнт (checkpoint) — сохранённая отметка прогресса, позволяющая продолжить с места остановки, а не с начала.
- Upsert — операция «вставить или обновить по ключу»; практический способ добиться идемпотентности в базе.
- Late-arriving data (опоздавшие данные) — события, доезжающие после закрытия периода; причина регулярного перезакрытия последних дней.
- Watermark (водяной знак) — граница «до этого момента данные считаем полными»; определяет, когда период можно закрывать.
- Catchup — режим планировщика, при котором он автоматически догоняет все пропущенные интервалы с даты старта.
- Mutual exclusion (взаимное исключение) — механизм, гарантирующий, что в критическую секцию зайдёт только один процесс; в скриптах реализуется файловым замком.
- Slowly changing dimension (медленно меняющееся измерение) — приём хранения истории изменений справочников; без него бэкфилл размечает прошлое сегодняшними справочниками.
Литература и источники
- Мартин Клеппман, «Designing Data-Intensive Applications» (2017, en; рус. «Высоконагруженные приложения», 2018). Главы про пакетную и потоковую обработку — лучшее объяснение, почему пересчёт истории и обработка потока должны быть одним кодом.
- Натан Марц, Джеймс Уоррен, «Big Data: Principles and best practices of scalable realtime data systems» (2015, en). Первоисточник лямбда-архитектуры; бэкфилл там — не процедура, а слой системы.
- Ральф Кимбалл, Марджи Росс, «The Data Warehouse Toolkit» (3-е издание, 2013, en; есть русские издания). Классика про витрины, исторические загрузки и медленно меняющиеся измерения.
- Документация Apache Airflow — airflow.apache.org. Разделы про
catchup,backfillиdata intervals. Самое практичное чтение: там описаны ровно те грабли, о которых эта статья. - Jay Kreps, «Questioning the Lambda Architecture» (O'Reilly Radar, 2014, en). Короткая заметка, объясняющая идею «бэкфилл = перемотка лога». Искать по названию — статья многократно перепубликована.
- Документация dbt — docs.getdbt.com, раздел про incremental models и
--full-refresh. Хороший пример того, как бэкфилл упаковывают в одну опцию.
Где встретилось у меня
Вчера разбирался с автоматическим сбором данных для одного из проектов: ежедневный ночной прогон работал, а историю за летние месяцы нужно было добирать отдельно — почти сотня дней, каждый примерно по десять минут. Отдельная обёртка шла помесячно от свежих месяцев к старым, ставила отметку на каждый закрытый день, при ошибке выжидала паузу и повторяла, а от столкновения с ежедневным прогоном её защищал общий замок. Попутно выяснилось, что замок на утилите flock вообще не работал — на macOS этой утилиты штатно нет, и отсутствие бинарника система честно принимала за «замок занят».
Краткое резюме
- Бэкфилл — это прогон обычной ежедневной логики по прошлым датам, чтобы закрыть дырку в истории или пересчитать её новым кодом.
- Он не восстанавливает прошлое, а вычисляет его заново сегодняшними правилами; сравнивая бэкфилл с ежедневными данными, легко сравнить несравнимое.
- Три обязательных элемента: идемпотентность (повтор не портит), чекпойнты (не начинать сначала), изоляция от боевого прогона и от лимитов источника.
- Опаснее падения — тихий ложный успех: пустой ответ источника, записанный как закрытый день. Проверяй не код возврата, а правдоподобность объёма.
- Прежде чем запускать бэкфилл на год назад, спроси, какое решение изменится от наличия этой истории. Часто честная отметка «данные с такой-то даты» дешевле и полезнее.