Databaser. Возвращение легкости стартапа
Всем привет, меня зовут Александр Даниленко, я — ведущий разработчик отдела «Бюджет-Online». В компании «БАРС Груп» работаю уже 5 лет. За это время нам удалось успешно исправить некоторые сложности процесса разработки. Первая проблема, с который мы столкнулись — развороты больших баз данных (БД) у разработчиков на локальных машинах. Сегодня мы расскажем об инструменте «Databaser», который на 100% позволяет ее решить.
Предыстория
Нужно пройти длинный путь взросления и становления любого проекта как инструмента, чтобы ему могли доверять пользователи. Но в большинстве случаев все приходят к одному результату — проект становится большим и сложным, с ним становится трудно работать.
Усложнение кодовой базы — это лишь верхушка айсберга. Не забывайте про тот объем данных, которые создают пользователи. С увеличением размера базы данных, количества таблиц и хранящейся в них информации, копятся не только корректные данные, но и ошибочные. Следует учитывать, что при многократных попытках исправления, необходимо заново разворачивать дамп базы данных.
Большой размер базы данных дает о себе знать при развороте на локальной машине разработчика. Ограничения отражаются на занимаемом месте на диске, скорости разворота дампа, качестве исполнения задач и взаимодействии с приложением, подключенным к нему. Кроме того, те ресурсы, которые выделяются на производственных серверах, несоизмеримы с возможностями локальной машины разработчика.
Ограничения
Для решения этой проблемы (для каждой производственной установки) поднимается несколько приложений в контуре разработки, с которыми предполагается работа как аналитиков (на развернутых приложениях), так и разработчиков (подключение напрямую к базам данных). Но время показало, что в этой схеме есть довольно большие ограничения — они касаются конкуренции между специалистами, а именно:
Обновление тестовых стендов может приводить к потере результатов работы и прерыванию деятельности других членов команды;
Приостановление функционирования тестовых стендов из-за ошибок при развороте данных, накатки ошибочных изменений в коде;
Искажение реальной картины относительно производственной установки при внесении изменений;
Снижение скорости работы из-за высокой нагрузки;
Возникновение блокировок.
О создании «Databaser»
Все эти ограничения послужили драйвером к реализации нового продукта «Databaser» с определенными требованиями:
Создание среза данных для указанных учреждений. В качестве учреждения может выступать заказчик или другая бизнес-единица, вокруг которой выстроены процессы;
Отсутствие привязки к конкретному проекту для дальнейшего распространения и применения;
Кроссплатформенность решения;
Возможность работы с Postgres, так как данная система управления базами данных (СУБД) является перспективной и используется во всех проектах нашего отдела;
Наличие опции многократного разворота полученной базы данных с минимальным участием разработчика;
Фиксация текущего состояния базы данных для дальнейшего ее использования с этого состояния при перезапуске;
Механизм удобного обмена срезами отдела между разработчиками (для экономии времени и ресурсов).
Мы обсуждали данный вариант решения проблемы с другими разработчиками, но наша идея была воспринята скептически. Нас это не остановило, и мы начали действовать.
Этапы разработки
Для достижения цели по переносу данных была выбрана технология контейнеризации Docker. Она позволяет создавать образы, при помощи которых в дальнейшем запускаются контейнеры, и есть возможность реализации образа на его основе. Язык программирования (для разработки самого инструмента) — Python 3 с библиотеками Asyncio и Asyncpg. СУБД Postgres является расширяемой и для нее существует множество плагинов. Один из них — Foreign Data Wrapper (FDW). Основная его задача — выполнение запросов к сторонней базе данных через локальную. В локальном Postgres создается схема, дублирующая схему на удаленном Postgres. Далее выполняются запросы к сэмулированной БД, которые перенаправляются в удаленную. Таким образом, можно реализовать выборку данных и запись этих данных в целевую БД. В качестве артефакта для распространения инструмента был выбран Docker-образ.
Таким образом во всей цепочке работы есть три элемента:
База данных донор (база-донор) — база приложения, из которой будет производиться выборка данных;
Целевая база данных — база данных со схемой, в которую должны «перекачиваться» данные. Может быть так, что данные из определенных таблиц не должны быть скачены, поэтому в схеме это должно быть отражено;
«Databaser» — приложение, используемое для переноса данных в целевую базу.
Postgres с целевой базой данных и «Databaser» запускаются в контейнерах. Postgres c базой-донором может быть запущен любым удобным способом. «Databaser» отвечает за генерацию SQL запросов, для генерации которых требуется схема базы данных. Для этого она динамически создается в начале работы приложения в виде Python-объектов.
Существует несколько основных классов:
База данных;
Таблица;
Колонка.
Схема базы данных в Python создается на основе схемы целевой базы данных в Postgres. Для воссоздания схемы БД, производится выборка таблиц и их колонок из служебных таблиц Postgres.
Запросы в базу данных являются неблокирующими операциями, которые позволяют переключаться на другие действия и продолжать исполнение. В случае возвращения результата из СУБД, можно возобновить с ним работу.
Сборка идентификаторов производится рекурсивно: существуют ограничения по глубине и стеку пройденных таблиц. Выборка идентификаторов записей таблиц, которые должны быть перенесены, происходит в определенном порядке:
Выбираются идентификаторы записей из ключевой таблицы;
Выбираются идентификаторы записей из таблиц, содержащих ссылку на ключевую таблицу;
Выбираются идентификаторы записей из остальных таблиц, отсортированных по алгоритму топологической сортировки по уровню связей между таблицами.
После сбора идентификаторов осуществляется генерация запросов на выборку данных из базы-донора и их записи в целевую БД. При формировании запросов идентификаторы бьются на чанки определенными частями, данные пишутся в целевую БД. В другом случае была бы совершена попытка выборки большого количества информации и их записи, что приводит к низкой скорости работы или к парализации работы инструмента.
Когда скачивание данных завершается, то производится корректная остановка контейнера с Postgres, на основе которого создается образ с определенным названием. В нашем случае указывается идентификатор задачи из Jira для выстраивания связи образа с задачей (для которой был разработан срез). После того как образ получен, он заливается в Registry — хранилище Docker-образов. Далее любой человек, имеющий доступ к Registry и конкретному образу, может скачать его себе на локальную машину и запустить контейнер. В итоге пользователь получает сильно урезанную базу, но со всеми необходимыми ему данными указанного учреждения, например.
Преимущества и первые результаты
Плюсы для разработчиков:
Теперь для пересоздания базы данных требуются считанные секунды, а не 10–12 часов.
Со срезом БД приложение работает очень быстро. Появляется ощущение, что ты работаешь со стартап-проектом — он не выглядит большим и массивным.
Скорость выполнения задач по исправлению ошибок прошлых лет возрастает, потому что разработчики не встречают прежних ограничений.
По причине того, что разработчики взаимодействуют только со своими срезами баз данных, снижается нагрузка на базы данных тестовых стендов, что положительно сказывается на производительности и качестве содержащейся в них информации.
Первоначально инструмент был предоставлен разработчикам для самостоятельного использования. В дальнейшем стало понятно, что способ запуска необходимо упростить и не стоит его выполнять на машине разработчика. Во время получения среза используются ресурсы компьютера разработчика, что отрицательно сказывается на его производительности.
Для организации CI в отделе используется Jenkins. Мы решили упростить запуск и создали Job. При сборке стали указывать несколько параметров и вся работа по получению среза производилась на удаленном сервере. Разработчику только оставалось дождаться получения среза базы данных и выполнить несколько дополнительных действий:
Скачать образ;
Запустить контейнер со срезом БД;
Подготовить конфигурационный файл для запуска приложения;
Приступить к работе.
Но стало ясно, что в Registry при активной работе стали копиться образы со срезами баз данных закрытых задач. Поэтому был реализован отдельный механизм, при помощи которого в автоматическом режиме производится зачистка устаревших образов. Это предотвращает заполнение жесткого диска.
Со временем также стало понятно, что ручной запуск получения среза является избыточным. К этому времени инструмент уже успел себя зарекомендовать. Следующим этапом стала автоматизация процесса сборки среза. Для этого потребовалось:
Внести изменение в описание задачи в Jira, где аналитики стали указывать параметры подключения к тестовому стенду в унифицированном виде;
Добавить несколько полей в саму задачу для указания состояния сборки и ссылку в Jenkins;
Сформировать фильтр задач Jira, из которого должны собираться задачи для получения среза БД;
Реализовать механизм для автоматического запуска сборки срезов БД.
Кроме того, в Telegram была создана отдельная группа, в которой разработчики, занимающиеся поддержкой «Databaser», информируют о запущенных сборках и возникающих проблемах. Таким образом мы оперативно реагируем на внештатные ситуации и находим выходы из них.
Далее был реализован инструментарий по упрощению подготовки окружения разработчика для решения задачи. При помощи одной команды, в которой разработчик указывает номер задачи, производится скачивание образа Postgres со срезом базы данных, запуск контейнера на его основе и генерация конфигурационного файла. Так, запустив одну команду, разработчик подготавливает свое окружение к работе и приступает к выполнению задачи. После того, как задача была выполнена, также при помощи одной команды, разработчик удаляет контейнер и образ со срезом базы данных. Это позволяет своевременно избавляться от артефактов старых задач на локальной машине.
Следующим логичным шагом является создание тестовых стендов для каждой задачи. Разработчик сможет оперативнее демонстрировать аналитику результаты своей работы, не дожидаясь слияния кода в основные ветки. Это положительно скажется на скорости принятия решений и стабильности приложения в целом. Кроме того, уровень конкуренции между аналитиками на тестовых стендах существенно снизится, так как основные проверки можно будет производить на отдельных стендах задач. На основных тестовых стендах будут выполняться финальные проверки, на предмет корректной работы в общей кодовой базе. Это задача будущего, которая обязательно будет реализована в жизнь.
Итог
Годы использования «Databaser» показали его эффективность: обновлялся он и зависимости, которые используются в работе. На данный момент все зависимости имеют актуальные версии, и мы планируем дальнейшую поддержку и развитие инструмента. Он имеет существенный потенциал для распространения в другие отделы.
С проектом можно ознакомиться на GitHub.