Привет! Меня зовут Антон, я ведущий программист департамента аналитических решений ЮMoney. Хочу поделиться историей о том, как наша команда ускорила один из самых рутинных процессов в работе дата-инженера — загрузку информации из нового источника данных в хранилище. Мы не просто оптимизировали процесс, а практически свели к нулю многодневную рутину и тем самым высвободили время для решения более интересных задач.

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

От монолита к платформе: почему рост данных убивал нашу скорость

Любое хранилище данных начинается с малого. Но со временем таблиц становится больше, а количество записей в них исчисляется миллионами и миллиардами. Начинаются трудности: полное резервное копирование БД растягивается на недели, восстановление из бэкапа не укладывается в SLA, производительность падает. Мы начали приближаться к этой критической точке и пошли по стандартному пути декомпозиции — разделили монолит на слои (stage, core, витрины), а с переходом на Apache Kafka добавили raw-слой для сырых данных и архив для долгосрочного хранения исходных JSON-сообщений.

Однако разделение на слои породило новую проблему. Теперь таблицы распределены по разным базам данных и могут располагаться на разных серверах, поэтому простым SQL-запросом перенести данные между ними стало невозможно. Для подключения каждого нового источника или даже внесения изменений в существующий приходилось проектировать таблицы для всех наших слоев и разрабатывать уникальные ETL-процессы для переливки данных между ними. Это съедало колоссальное количество времени.

По ссылке из QR-кода видеозапись доклада о декомпозиции монолита в DWH
По ссылке из QR-кода видеозапись доклада о декомпозиции монолита в DWH

Что показывала статистика

Несмотря на зрелость компании, новые источники данных появляются постоянно. За год мы насчитали 13 задач по их подключению. Каждая занимала в среднем полторы недели. А еще были десятки задач по доработке существующих источников (изменение и добавление атрибутов), каждая из которых отнимала 1-2 дня.

График трудозатрат рисовал неутешительную картину: с каждым новым источником мы поднимались по лестнице, где затраты на поддержку только росли. Это вело к необходимости постоянно расширять штат, а это тупиковый путь. В итоге мы решили поставить амбициозную цель: сократить время на подключение нового источника на 80%. Для этого требовался не просто косметический ремонт, а смена парадигмы.

Вжух: вам понадобится два волшебных ингредиента

Мы решили автоматизировать процесс через платформенный подход и кодогенерацию. Основная ид��я: система автоматически генерирует объекты баз данных и ETL-пайплайны. Но для того, чтобы эта «магия» сработала, нужен главный ингредиент — метаданные источника.

Нам повезло: в компании был принят specification-first подход. Сначала создаётся спецификация топика в Kafka (в нашем случае в формате OpenAPI), а уже потом по ней формируется код. Однако для дата-инженерии стандартных описаний полей и типов недостаточно. Пришлось договориться с поставщиками данных о предоставлении критически важных параметров:

  • Первичные ключи. Без их явного указания невозможно обеспечить даже первую нормальную форму.

  • Обязательность заполнения полей. Это напрямую влияет на производительность запросов и их структуру.

  • Ограничения для длины строк. Использовать VARCHAR(MAX) нужно только тогда, когда на это есть веские основания. В остальных случаях заранее договоритесь о максимальной длине полей.

  • Оптимальные числовые типы. На миллиардах строк разница даже в несколько байт между smallint и int превращается в значительную экономию дискового пространства.

  • Отказ от цепочки ссылок на другие спецификации и вариативных блоков. Спецификация должна быть самодостаточной и покрывать весь набор данных.

  • Описания полей для каждого атрибута. Идеально, если они приходят прямо из спецификации.

Главное — эти договорённости должны соблюдаться.

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

Выбор инструмента: гипотезы и главное решение

Нашим основным источником данных была Kafka, основная экспертиза у разработчиков — MS SQL и С#. Исходя из этого, на этапе аналитики мы проработали несколько гипотез по двум направлениям работы.

Чтение данных из Kafka:

  1. Доработка своего legacy-кода. Решение дорогое и неэффективное.

  2. Готовые инструменты (Kafka Connect). Отпали из-за отсутствия экспертизы по Java в команде и высоких рисков поддержки.

  3. Open-source фреймворки. Мы нашли KafkaFlow, который нам очень понравился.

Перенос данных между слоями:

  1. Использование DAG’ов для Airflow. Интерпретируемый Python-код на наших замерах показал более низкую производительность, чем решение на .NET.

  2. Инструменты вроде DBT. Решали задачу лишь частично на тот момент, но позже мы нашли перспективную связку DBT + Trino.

  3. Кодогенерация ETL на .NET. Наш эксперт подготовил отличный прототип, который мы и взяли за основу.

Мы распараллелили эксперименты и договорились о единых критериях оценки. В итоге победила комбинация: свой ETL на .NET и использование библиотеки KafkaFlow для чтения данных из источника.

Как выглядит финальный пайплайн

Вместо сложной системы с микросервисной архитектурой в Kubernetes мы пошли прагматичным путём и используем уже существующий CI/CD-контур: контейнер с API для кодогенерации + Jenkins.

Теперь процесс выглядит так:

  1. Выбор спецификации. Дата-инженер через простой интерфейс выбирает нужную спецификацию из общего репозитория и конкретные топики внутри неё.

  2. Обработка метаданных. Спецификация автоматически конвертируется в наш внутренний формат метаданных.

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

  4. Кодогенерация. Система создаёт SQL-скрипты для объектов на всех слоях хранилища, конфигурационные файлы и код ETL-сервисов.

  5. Pull Request. Сгенерированный код автоматически отправляется в виде Pull Request’ов в соответствующие репозитории системы хостинга кода.

  6. Ревью и деплой. Разработчик проверяет PR, апрувит и мёрджит его. Далее стандартный CI/CD-пайплайн разворачивает изменения на боевых серверах.

Вся «магия» кодогенерации вынесена в отдельный контейнер, что закладывает основу для будущей миграции в Kubernetes.

Результаты: от 1,5 недель к 4 часам

В итоге мы сократили время от поступления задачи на разработку до развертывания релиза на боевых серверах на 98% — с 10 дней до 4 часов.

Первый запуск был показательным, потому что мы предоставили нашу платформу стажёру. Он только-только попал в наш департамент и никогда не решал подобных задач. Суммарно на загрузку данных из нового источника ему понадобилось — 4 часа 12 минут. Чистое время работы стажёра вместе с чтением документации — 51 минута. Остальное время — это CI/CD-процесс, тестирование и релизный цикл.

Повторный запуск опытным инженером составил 3 часа 1 минуту общей работы, из которых 30 минут — его личное участие.

Выводы

Платформенный подход решает проблему постоянного расширения штата разработчиков для поддержки растущего числа ETL-процессов и снижает Lead Time.

В самом начале стоит договориться о контрактах и формате метаданных. Без чётких, соблюдаемых спецификаций и договоренностей с поставщиками данных о кодогенерации не может быть и речи. Рекомендую использовать промежуточный внутренний формат контрактов, чтобы отвязаться от изменений на стороне источника.

Автоматизируйте повторяющиеся задачи. Если вы делаете что-то более 3-5 раз — это кандидат на автоматизацию. Не тратьте время высококлассных специалистов на рутину. Чем раньше удастся автоматизировать процесс, тем быстрее получится окупить затраты на этот проект.

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

Главный посыл: освободите себя для интересных задач. Анализируйте свой workflow, находите точки боли, которые повторяются из раза в раз, и смело автоматизируйте их. Это не только экономит время, но и делает работу инженера по-настоящему увлекательной.