Привет! Меня зовут Андрей, я Data Engineer в компании GRI. Мы занимаемся заказной разработкой, и один из наших ключевых клиентов — Sunlight. Я расскажу, как не тратить время на рутину в процессах и автоматизировать всё, что только можно. Это будет особенно актуально тем, кто в компании solo Data Engineer.

С чего всё началось

Компания Sunlight, как и любая другая большая организация, на определённом этапе столкнулась с необходимостью хранения большого объёма данных, из которых можно извлекать нужные для бизнеса показатели, строить ML-модели и находить полезные инсайты.

Если у вас есть мобильное приложение и миллионы пользователей, ежедневно генерирующих данные, то без хранилища не обойтись. Данные копятся лавинообразно.

Скорее всего, вы столкнетесь с классическими проблемами:

  1. Как забирать? Для этого необходим ETL-процесс, подходящий для вашего бизнеса.

  2. Где хранить? Чтобы хранить значительный объём данных, нужен кластер.

  3. Проблемы с качеством данных, в том числе в условиях внезапных изменений во внешних источниках.

Меня пригласили в компанию как раз для того, чтобы построить хранилище данных, решающее эти проблемы. Выбор пал на ClickHouse ещё до моего прихода.

Почему именно он? Среди open source-решений ClickHouse — один из лучших. Он обеспечивает очень высокую скорость обработки запросов, но вместе с тем требует множество ручных настроек.

Никаких костылей

С самого начал я решил, что будет проще и надёжнее сразу пойти по пути полной автоматизации загрузки данных в DWH, и разработал собственный фреймворк.

Без автоматизации мы рано или поздно столкнулись бы с ситуацией, когда множество DAG’ов содержали бы дублирующийся код. Это затруднило бы отслеживание изменений, внесение правок и поиск ошибок, создавая дополнительные сложности в поддержке системы.

Этот фреймворк генерирует все процессы и зависимости на основании небольшого YAML-файла. Его структура включает в себя следующие компоненты: 

  1. Вид пайплайна: загрузка данных, API, построение витрин и т.д.;

  2. Источник: источник данных для загрузки или обработки;

  3. Конфигурация: как��е-либо специфические настройки данного процесса;

  4. объекты: список объектов для загрузки или список витрин с их персонализированными настройками.

Главный принцип — никакого ручного «костылестроения». Если фреймворк что-то не умеет, то мы расширяем его, а не делаем ручные правки.

Постепенно развивали фреймворк. Начали с загрузки различных API — например, data stream Яндекса и других. Потом подключили различные СУБД внутренних сервисов и аналитические песочницы: PostgreSQL, MSSQL, ClickHouse и прочие. В конфигурации есть:

  1. общие параметры для всего DAG’а, такие как расписание, идентификатор соединения и некоторые другие;

  2. параметры загрузки и её настроечные параметры, которые будут использованы по умолчанию для таблиц фреймворка — это стратегия загрузки и окно пересчёта.

Большинство параметров опциональны и не требуют заполнения. Также обеспечена приоритизация параметров загрузки. Самый большой вес приходится на параметры на уровне самой таблицы, а далее — параметры уровня 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 ноября, а в случае необходимости продления всегда можно её переименовать.

Как это выглядит сейчас и выводы 

На текущем этапе у нас загружены сотни таблиц-источников, поверх которых построено множество регулярно обновляемых витрин.

Изначально команда состояла из одного меня. Мне пришлось самостоятельно разбираться в источниках данных, продумывать и создавать архитектуру. Постепенно поступали новые задачи, источники, новые требования. Это закономерно привело к расширению системы и пополнению команды. На текущий момент у нас два инженера и три аналитика.

Сегодня фреймворк стабильно работает и продолжает развиваться.

Комментарии (0)