Pull to refresh

Мир должен знать, как я генерировал mock-объекты

Reading time5 min
Views6.4K

Поставил Linux? Напиши об этом статью. Нашел на Github интересный проект? Напиши об этом статью. Примерная такая логика привела к написанию этой статьи.

Недавно мне потребовалось наполнить базу данных из более чем 300 таблиц со сложной структурой и кучей внешних ключей тестовыми данными. Требования к наполнению минимальны, нужны просто таблицы с заполненными полями, семантика пока не важна. Сделать надо быстро, с минимальными усилиями и как можно ближе к уровню СУБД.

Итак, что мы имеем: база на Postgres, которую надо быстро, дешево и сердито наполнить данными, достаточными для того, чтобы проверить корректную работу пользовательского интерфейса. Интернет открывает перед нами просто прорву инструментов для генерации mock-объектов: DBeaver, Datanamic Data Generator, Mockaroo, Faker.js, в общем, тысячи их.

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

Проект называется mock-data, написан на Go, развивается с 2017 года. Звезд и контрибьюторов не много, гуглится плохо, зато Readme обещает, что достигнуть нужного эффекта можно будет вызовом буквально пары команд.

Заявлено, что mock-data работает с PostgreSQL и Greenplum Database. Разработчики предупреждают, что в случае наличия кастомных типов и ограничений ссылочной целостности на таблицах возможны ошибки. В моем случае проблемы были, но не критичные.

Mock-data может генерировать данные для всей базы, выбранной схемы или отдельной таблицы. Можно указать количество генерируемых записей, оно одинаково для всех таблиц и по умолчанию равно 10. Еще можно включить вывод отладочных сообщений и пропустить восстановление удаляемых ограничений ссылочной целостности.

Работа mock-data состоит из следующих шагов:

  • Разбираются аргументы командной строки, проверяется подключение к базе и наличие таблиц.

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

  • Все ограничения ссылочной целостности удаляются.

  • Производится заполнение таблиц.

  • Ограничения ссылочной целостности восстанавливаются, производится проверка того, что сгенерированные данные удовлетворяют ограничениям целостности.

Что ж, начнем. Поскольку статья заявлена как туториал, постараюсь описать все максимально детально.

Разработчики предупреждают, что использовать mock-data на проде нельзя. Поэтому создаем локальную копию базы. Поскольку кое-какие данные у меня уже есть, то для создания локальной копии я воспользовался pg_dump с флагом --schema-only. При загрузке тестовых данных обратно это позволит избежать проблемы с дублированием первичных ключей.

Здесь стоит сказать, что настоятельная рекомендация не использовать mock-data на проде связана с тем, что, как было сказано выше, перед генерацией данных удаляются все ограничения ссылочной целостности. Конечно, потом они будут восстановлены, но каждое ограничение удаляется в отдельной транзакции и если что-то при удалении идет не так, то работа программы останавливается и вы остаетесь с базой, где часть ограничений удалена, а часть осталась. Хорошего в этом мало. Будьте готовы к тому, что базу (или отдельные схемы) придется удалять и создавать заново.

Локальная копия развернута. Пора заняться самим mock-data. Нам предлагается либо скачать бинарник, либо воспользоваться докер образом. Я выбрал докер.

Скачиваем образ:

docker pull ghcr.io/pivotal-gss/mock-data:latest

Создаем для него тэг:

docker image tag ghcr.io/pivotal-gss/mock-data mock

Cоздаем локальную директорию для временных файлов:

mkdir /tmp/mock

Когда я проделал эти действия, мои ладошки вспотели от предвкушения того, как моя база наполнится моками. Но не тут-то было, пришлось пройтись по кое-каким граблям. Спешу поделиться, по каким.

Начнем с очевидного. Коль уж обращение к локальной базе данных будет происходить из контейнера, то в postgresql.conf надо разрешить подключение не только с локалхоста. У себя я разрешил подключения с любых адресов:

listen_addresses = '*'

Напомню, что соответствующие настройки надо делать и в pg_hba.conf.

Не забываем, что в таком случае нам надо сделать родительский хост доступным для контейнера (UPD: в комментариях подсказывают более изящные решения), то есть при запуске докера указать параметр --network="host".

Раз уж мы начали править postgresql.conf, то настоятельно рекомендую в качестве языка сообщений установить английский:

lc_messages = 'en_US.UTF-8'

Зачем это делать? А затем, что если оставить язык сообщений, например, русским, то в какой-то момент mock-data упадет с сообщением примерно следующего вида:

Encountered error when removing constraints for table Ошибка запроса: ERROR: отношение "Имя ограничения" не существует.

Что же происходит? Все просто. Как уже говорилось, перед созданием тестовых данных каскадно удаляются все ограничения ссылочной целостности. Поэтому в какой-то момент mock-data пытается удалить уже не существующие отношения. Если посмотреть в исходники, то видно, что разработчики так и задумали. Но если посмотреть внимательнее, то становится понятно, что игнорируются только те ошибки, текст которых содержит подстроку "does not exist". Логично, что если язык сообщений у вас русский, то сообщение будет несколько другим.

Также рекомендую проверить длину имени всех ограничений. Хотя Postgres позволяет задавать длину имени до 63 символов, в моем случае mock-data радостно упал на имени длиной 54 символа. Пришлось укоротить, благо, копия локальная и проблемным было всего одно имя.

Выполняем команду:

docker run --network="host" -v /tmp/mock:/home/mock mock schema -n ИМЯ_СХЕМЫ --uri postgres://ПОЛЬЗОВАТЕЛЬ:ПАРОЛЬ@ХОСТ:ПОРТ/БД

И видим, что процесс пошел. Выводятся сообщения о чтении метаданных, а далее для каждой таблицы отдельно выводится прогресс заполнения данными.

Заполнение данными началось
Заполнение данными началось

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

Успешное завершение
Успешное завершение

А в таблицах будут данные, похожие на те, что представлены на рисунке ниже. Отмечу, что после завершения генерации я проверил, соответствуют ли сгенерированные данные ограничениям ссылочной целостности. Проверить можно выполнив несколько запросов с соединением таблиц по внешним ключам. В моем случае все было хорошо, джойны джойнились, а foreing key ссылались на существующие строки в других таблицах.

Пример данных в таблице
Пример данных в таблице

Конечно, данные не реалистичны, но для первичного тестирования вполне подойдут.

Чтобы залить полученные данные на условный прод (или удаленную базу данных) сделаем дамп локальной базы. Естественно, с флагом --data-only. Если вдруг в базе есть циклические ссылки или есть опасения в том правильно ли загрузятся данные, рекомендую в файле, полученном pg_dump, установить SET CONSTRAINTS ALL DEFERRED. И провести загрузку файла с флагом --single-transaction. В моей баз циклические ссылки были и указанный метод помог.

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

Будет здорово, если в комментариях вы поделитесь своим опытом создания моков на уровне СУБД.

Tags:
Hubs:
Total votes 7: ↑5 and ↓2+3
Comments10

Articles