Привет, меня зовут Юля, я дата-инженер в департаменте аналитических решений ЮMoney. Мы разрабатываем и поддерживаем ETL (ELT)-процессы загрузки данных для BI-продуктов компании: отчётов, дашбордов, витрин, API и других.

В этой статье поделюсь, как мы разрабатывали MVP для построения data lineage-системы и к каким выводам пришли, — так вы сможете адаптировать подход под свои процессы.

Проблема, которая привела нас к разработке MVP, заключается в том, что в начале работы над большинством задач требуется подготовительная аналитика: восстановить путь происхождения данных, чтобы понимать, какие ETL (ELT)-процессы будут затронуты и на что повлияют изменения. Это увеличивает время выполнения задачи. Но, что ещё существеннее, процесс достаточно рутинный: каждый раз необходимо проводить статический анализ кода и изучать документацию (при её наличии). Мы решили автоматизировать эту работу, чтобы иметь под рукой инструмент для построения пути данных по точкам их обработки и применения, — иными словами создать data lineage-систему.

Подходы к построению data lineage-систем

Data lineage-системы можно строить:

  1. Вручную — через явное указание зависимостей в данных.

  2. Через описание метаданных (например, в yml-файлах или jinja-шаблонах), которые затем можно загрузить, например, в dbt и использовать для визуализации движения данных.

  3. Автоматически, путём разбора метаданных из кода и их визуализации.

Первый вариант возможен для реализации, если требуется построить data lineage однократно или для ограниченного набора бизнес-процессов, так как при любых дальнейших изменениях в ETL (ELT)-процессах загрузки и использования данных потребуется вручную актуализировать полученный data lineage.

Второй и третий варианты позволяют автоматизировать часть работы. Однако второй вариант с подготовкой файлов с метаинформацией накладывает дополнительную нагрузку на разработчиков по созданию своего рода «документации». Поскольку увеличивать объём ручной работы не хотелось, мы остановились на третьем варианте.

Наш стек и реализация

Мы не хотели тратить более одного квартала на разработку MVP, поэтому выбрали один из основных кейсов движения данных от источника до пользовательского продукта: получение сырых данных из Kafka организация хранения в таблицах stage-слоя упорядочивание и преобразование в таблицах DWH сбор витрин для пользователей.

MVP реализовали на .NET 8. Парсинг кодовой базы решили проводить с использованием библиотеки DacFx (подробнее о преимуществах парсера — в статье коллеги «Какой парсер для автоматизации ревью кода лучше — DacFx или ANTLR»).

Для хранения полученной метаинформации выбрали графовую базу данных Neo4j. Графовые базы данных обеспечивают гибкость схемы, не требуя её обновления при добавлении новых связей. Кроме того, Neo4j поддерживает язык Cypher, позволяющий создавать запросы для визуализации процессов с участием данных, необходимых для решения конкретной задачи.

В начале разработки мы подготовили метамодель хранения данных: выделили основные сущности типа data (Topic — топик Kafka, Table — таблица базы данных, DataMart — витрина данных) и сущности типа transport для определения связей между ними (KafkaService — сервис разбора данных из топика Kafka, Procedure — хранимая процедура, участвующая в ETL (ELT)-процессе, и так далее).

На первой итерации мы выделили основные типы связей между объектами: EXEC, USE и другие, полагая, что это облегчит написание Cypher-запросов. Однако в процессе разработки поняли, что для нашей реализации достаточно только направления связи (пример одного из Cypher-запросов можно будет увидеть в разделе ниже).

На этапе MVP результаты предполагались к использованию только дата-инженерами нашего департамента, поэтому для визуализации data lineage мы использовали встроенный Neo4j browser, позволяющий отображать конкретные кейсы движения данных через выполнение Сypher-запросов.

Схема работы MVP приведена на рисунке ниже.

Результат работы

Вот один из примеров построенного data lineage (скриншот из Neo4j browser): на нём можно проследить путь от источника — топика Kafka — до витрин, использующих данные этого топика. Стрелки показывают направление движения данных.

Работая с MVP со стороны пользователей, мы поняли, что нам необходим набор эффективных Cypher-запросов. В частности, полезны встроенные функции — shortestPath для поиска кратчайшего пути между двумя узлами и allShortestPaths для поиска всех кратчайших путей.

Для построения data lineage, как на рисунке выше, использовался следующий Cypher-запрос:

Для получения кратчайшего пути нужно указать название топика Kafka (сущность Topic, атрибут name), в квадратных скобках — количество ребёр в цепочке (в нашем случае — от 1 без верхнего ограничения), направление движения данных (направление стрелки) и конечный объект (витрина данных — сущность DataMart).

В процессе разработки MVP мы тестировали сервис на реальных кейсах заказчиков. Например, запросы вида «какой топик является источником для витрины данных или отчёта» решаются за несколько минут — достаточно получить итоговый data lineage и отсечь связи, не относящиеся к задаче. Без MVP ответ требует поиска в кодовой базе или документации, что занимает от получаса до нескольких часов — в зависимости от сложности задействованных ETL (ELT)-процессов.

К чему пришли и что планируем делать дальше

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

Рекомендуем внедрение data lineage-системы компаниям, где уже реализована или внедряется система data governance, поскольку data lineage является её неотъемлемой частью.


Поделитесь опытом — реализовывали ли вы подобные системы? С какими проблемами сталкивались при разработке и используете ли эти системы в повседневной работе?