
Привет! Меня зовут Андрей, я Data Engineer в компании GRI. Мы занимаемся заказной разработкой, и один из наших ключевых клиентов — Sunlight. Я расскажу, как не тратить время на рутину в процессах и автоматизировать всё, что только можно. Это будет особенно актуально тем, кто в компании solo Data Engineer.
С чего всё началось
Компания Sunlight, как и любая другая большая организация, на определённом этапе столкнулась с необходимостью хранения большого объёма данных, из которых можно извлекать нужные для бизнеса показатели, строить ML-модели и находить полезные инсайты.
Если у вас есть мобильное приложение и миллионы пользователей, ежедневно генерирующих данные, то без хранилища не обойтись. Данные копятся лавинообразно.
Скорее всего, вы столкнетесь с классическими проблемами:

Как забирать? Для этого необходим ETL-процесс, подходящий для вашего бизнеса.
Где хранить? Чтобы хранить значительный объём данных, нужен кластер.
Проблемы с качеством данных, в том числе в условиях внезапных изменений во внешних источниках.
Меня пригласили в компанию как раз для того, чтобы построить хранилище данных, решающее эти проблемы. Выбор пал на ClickHouse ещё до моего прихода.
Почему именно он? Среди open source-решений ClickHouse — один из лучших. Он обеспечивает очень высокую скорость обработки запросов, но вместе с тем требует множество ручных настроек.
Никаких костылей
С самого начал я решил, что будет проще и надёжнее сразу пойти по пути полной автоматизации загрузки данных в DWH, и разработал собственный фреймворк.
Без автоматизации мы рано или поздно столкнулись бы с ситуацией, когда множество DAG’ов содержали бы дублирующийся код. Это затруднило бы отслеживание изменений, внесение правок и поиск ошибок, создавая дополнительные сложности в поддержке системы.
Этот фреймворк генерирует все процессы и зависимости на основании небольшого YAML-файла. Его структура включает в себя следующие компоненты:

Вид пайплайна: загрузка данных, API, построение витрин и т.д.;
Источник: источник данных для загрузки или обработки;
Конфигурация: как��е-либо специфические настройки данного процесса;
объекты: список объектов для загрузки или список витрин с их персонализированными настройками.
Главный принцип — никакого ручного «костылестроения». Если фреймворк что-то не умеет, то мы расширяем его, а не делаем ручные правки.
Постепенно развивали фреймворк. Начали с загрузки различных API — например, data stream Яндекса и других. Потом подключили различные СУБД внутренних сервисов и аналитические песочницы: PostgreSQL, MSSQL, ClickHouse и прочие. В конфигурации есть:
общие параметры для всего DAG’а, такие как расписание, идентификатор соединения и некоторые другие;
параметры загрузки и её настроечные параметры, которые будут использованы по умолчанию для таблиц фреймворка — это стратегия загрузки и окно пересчёта.
Большинство параметров опциональны и не требуют заполнения. Также обеспечена приоритизация параметров загрузки. Самый большой вес приходится на параметры на уровне самой таблицы, а далее — параметры уровня DAG’а. В последнюю очередь параметры по умолчанию для всего фреймворка.
Особое внимание мы уделили гибкости раздела зависимостей: их можно построить как по дате отработки запроса, так и по произвольному инкременту, и на всякий случай можно даже указать произвольный фильтр.
При заполнении зависимостей фреймворк сам генерирует SQL-запросы, которые будут проверять готовность данных на стороне источника, для любых внутренних таблиц подтягивается автоматически зависимость от выполнения соответствующей задачи на наполнение объекта.
tables:
table_name:
date_col: для расчёта по окну указываем колонку с датой
recalc_interval: интервал для пересчёта по окну
custom_field: для расчёта по инкременту
recalc_strategy: тип стратегии пересчёта
dependencies:
dependency_table_name:
date_col: для проверки наличия данных на дату выполнения ETL
database: база данных, в которой лежит проверяемая таблица
custom_where: для указания особых условий готовности
custom_field: поле для проверки наличия данных по инкременту
Отображение данных для аналитиков и менеджеров
С самого начала я держал в голове идею, что требуется сделать максимально дружелюбный интерфейс создания витрин для аналитиков и менеджеров, которые неплохо умеют писать SQL, но тонкости ETL/ELT-процессов им не требуются.
Поэтому с помощью Gitlab мы написали комплексный CI-процесс автоматизации.
Для добавления витрин достаточно положить SQL-запрос в нужную папку и создать merge-request, в рамках которого CI проверит корректность SQL, создаст DDL-файл, распарсит запрос и построит зависимости. От пользователя требуется только выбрать необходимую стратегию пересчёта — инкрементальный, пересчёт окном за период, полный пересчёт таблицы — и отдать на проверку. Однако можно и вручную внести правки в YAML, если нужны нестандартные параметры.

Ручные проверки merge request у нас двусторонние: техническая со стороны инженеров, а также проверка логики со стороны владельца данных.
Работу с фреймворком максимально упростили, провели несколько внутренних обучающих сессий. Получились внутренние встречи в формате интерактива «как самостоятельно добавить витрину», которые, разумеется, дополнили пошаговой инструкцией и документацией.
При написании фреймворка я старался всё максимально типизировать, для каждого источника использовал двухбуквенный префикс, который проставляется в его название, в название объектов, созданных из него, в наименование DAG’а, в теги для быстрого поиска и в ролевой модели DWH, и т. д.
На текущий момент в нашем контуре десятки источников, но мы продолжаем развиваться по мере потребностей бизнеса. Недавно для BI-отдела добавили фичу: когда витрина обновилась, фреймворк сам уведомляет BI-систему: «данные обновлены, можно тянуть». Если нет ответа — пробует ещё раз. Если не получилось — оповещение. Если всё в порядке, то BI перетягивает и пишет в лог, что всё успешно.
На днях нас попросили добавить возможность загрузки данных из Google Таблиц, что мы тоже успешно автоматизировали. Позже появилась задача забирать данные из Redis. Параллельно мы ускорили фреймворк и подружились с новыми API.
Развитие фреймворка
Фреймворк развивали итеративно. Сначала — просто загрузка ,потом — витрины, затем — BI. Всё расширяли по мере задач от бизнеса, но, опять же, не прибегая к костыльным решениям. Например, сначала загрузка и витрины были в одном пайплайне, но такой подход оказался слишком тяжёлым, поэтому разделили:
DataFlow — для загрузки и трансформаций;
DataMarts — для витрин.
Исторические DAG’и тоже предусмотрены. Если нужно перегрузить таблицу (например, добавили новые поля), то в YAML «создаётся» DAG с префиксом history, в котором указываем нужный интервал пересчёта, а также витрины, которые требуется перегрузить. При этом, если пересчёт требует некую другую DML, а не ту, которая используется в регулярной загрузке, то достаточно её положить в папкуdagname_history. Далее фреймворк проверит, если там есть файл, то запускает его, если нет — регулярная загрузка.
Без хаоса не обошлось
Ради обеспечения целостности данных наша ролевая модель не позволяет обычным пользователям модифицировать данные в технических и слоях хранения, но при этом, разумеется, есть «песочница», где каждый может создавать объекты в своё удовольствие. Мы сделали её чтобы пользователи могли выполнять ad-hoc аналитику или делать макеты витрин. Изначально подразумевалось, что люди будут чистить за собой, то есть удалять неактуальные таблицы. Как оказалось, зря.
Некоторое время назад количество «брошенных» объектов в песочнице перевалило за 150, и тогда мы добавили новый оператор, который проходит по ночам и удаляет таблицы, которыми, согласно логам, не пользовались некоторое время. При этом, если пользователю требуется подержать таблицу подольше, то достаточно указать срок годности в названии таблицы, например, table_name_20251101 будет жить до 1 ноября, а в случае необходимости продления всегда можно её переименовать.
Как это выглядит сейчас и выводы
На текущем этапе у нас загружены сотни таблиц-источников, поверх которых построено множество регулярно обновляемых витрин.
Изначально команда состояла из одного меня. Мне пришлось самостоятельно разбираться в источниках данных, продумывать и создавать архитектуру. Постепенно поступали новые задачи, источники, новые требования. Это закономерно привело к расширению системы и пополнению команды. На текущий момент у нас два инженера и три аналитика.
Сегодня фреймворк стабильно работает и продолжает развиваться.