Рубрики
Без рубрики

Как запустить тесты интеграции баз данных в 20 раз быстрее

Автор оригинала: Vlad Mihalcea.

Вступление

Базы данных в памяти, такие как H2 , HSQLDB и Derby , отлично подходят для ускорения интеграционных тестов. Хотя большинство запросов к базам данных могут выполняться с этими базами данных в памяти, многие корпоративные системы используют сложные собственные запросы, которые могут быть протестированы только на реальных производственных реляционных базах данных.

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

Тесты на гибернацию

Hibernate по умолчанию использует H2, и выполнение всех тестов для модуля документации (317 тестов) занимает около 46 секунды:

> gradle clean test

:documentation:processTestResources
:documentation:testClasses
:documentation:test

BUILD SUCCESSFUL

Total time: 46.148 secs

MySQL

Теперь давайте посмотрим, сколько времени требуется для выполнения всех этих тестов в моем локальном ядре базы данных MySQL 5.7:

> gradle clean test -Pdb=mysql

:documentation:processTestResources
:documentation:testClasses
:documentation:test

BUILD SUCCESSFUL

Total time: 30 mins 26.568 secs

Операторы DDL MySQL очень дороги, и каждый модульный тест создает и уничтожает SessionFactory , который, в свою очередь, создает и уничтожает схему базы данных. Это позволяет каждому тесту начинаться с чистого состояния, обеспечивая тем самым изоляцию теста.

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

К счастью, для интеграционных тестов нам не нужна гарантия долговечности. Нам нужна только скорость!

Тем не менее, мы можем переместить каталог данных базы данных на диск оперативной памяти. В Linux вы можете использовать temps , но , поскольку у меня машина с Windows, я собираюсь использовать для этой цели драйвер виртуального диска ImDisk.

Если вы заинтересованы в ускорении тестов интеграции баз данных с Docker и tmpfs, ознакомьтесь с этой статьей . Он работает в любой операционной системе (Linux, OSX, Windows), и даже для Windows работать с ним намного проще, чем с драйвером виртуального диска ImDisk.

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

Сценарий, который выполняет всю работу, выглядит следующим образом:

sc stop %MYSQL_SERVICE%

imdisk -D -m R:
imdisk -a -s 2G -m R: -P  -p "/FS:NTFS /C /Y"

mkdir R:\data
xcopy "%MySQL_DATA%\data" "R:\data" /S /E

"%MySQL_HOME%\bin\mysqld" --defaults-file="%MySQL_DATA%\my_ram.ini"
  1. Во-первых, я останавливаю службу MySQL по умолчанию.
  2. Затем я сопоставляю 2 Гб ОЗУ (например, R:\ ) и форматирую его как NTFS.
  3. После этого я копирую папку данных MySQL на новый диск в памяти.
  4. Наконец, я просто запускаю новый демон MySQL, используя файл конфигурации, в котором каталог данных настроен следующим образом:
# Path to the database root
datadir=R:/data

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

"%MySQL_HOME%\bin\mysqladmin" -u mysql -p shutdown

imdisk -D -m R:

sc start %MYSQL_SERVICE%

Теперь выполнение тестов в модуле документации Hibernate занимает менее 2 минут:

> gradle clean test -Pdb=mysql

:documentation:processTestResources
:documentation:testClasses
:documentation:test

BUILD SUCCESSFUL

Total time: 1 mins 41.022 secs

Мы можем сделать лучше, чем это. Как уже объяснялось ранее, нам вообще не нужна долговечность, поэтому я собираюсь изменить некоторые конфигурации MySQL, которые описаны в этой очень хорошо написанной статье Percona :

log-output=NONE
slow-query-log=0
innodb_flush_log_at_trx_commit=2
innodb_log_buffer_size=3M
innodb_buffer_pool_size=180M

Повторите наши тесты, и мы получим:

Total time: 1 mins 30.628 secs

Это очень 20 улучшение времени по сравнению с конфигурацией ядра базы данных MySQL по умолчанию.

PostgreSQL

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

Запуск тестов документации на PostgreSQL, берет на себя 3 минуты с использованием настроек по умолчанию:

> gradle clean test -Pdb=pgsql

:documentation:processTestResources
:documentation:testClasses
:documentation:test

BUILD SUCCESSFUL

Total time: 3 mins 23.471 secs

Чтобы запустить новый демон PostgreSQL, работающий на диске в памяти, нам нужно использовать следующий пакетный сценарий:

sc stop %PGSQL_SERVICE%

imdisk -D -m R:
imdisk -a -s 2G -m R: -P  -p "/FS:NTFS /C /Y"

mkdir R:\data
xcopy "%PGSQL_DATA%" "R:\data" /S /E

"%PGSQL_HOME%\bin\pg_ctl" start -D R:\data

Когда мы закончим тестирование, мы сможем остановить демона PostgreSQL и запустить службу по умолчанию следующим образом:

"%PGSQL_HOME%\bin\pg_ctl" stop -D R:\data

imdisk -D -m R:

sc start %PGSQL_SERVICE%

Повторяя тесты документации, мы получаем следующие результаты:

> gradle clean test -Pdb=pgsql

:documentation:processTestResources
:documentation:testClasses
:documentation:test

BUILD SUCCESSFUL

Total time: 1 mins 45.431 secs

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

fsync = off
synchronous_commit = off
full_page_writes = off

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

sc stop %PGSQL_SERVICE%

imdisk -D -m R:
imdisk -a -s 2G -m R: -P  -p "/FS:NTFS /C /Y"

mkdir R:\data
xcopy "%PGSQL_DATA%" "R:\data" /S /E
xcopy postgresql.conf "R:\data" /Y 

"%PGSQL_HOME%\bin\pg_ctl" start -D R:\data

На этот раз мы получаем следующие результаты:

Total time: 1 mins 37.935 secs

Это решение не ограничивается только ОС Windows. Вы можете достичь той же цели с помощью Docker и tmpfs в любой операционной системе. Для получения более подробной информации ознакомьтесь с этой статьей .

Модуль документации крошечный по сравнению с hibernate-core , который в настоящее время содержит 4352 модульных теста. С учетом этих оптимизаций выполнение тестов hibernate-core занимает:

5 минут 34,711 секунды 7 минут 55,082 секунды 8 минут 34,275 секунды

Вывод

Хотя и не так быстро, как H2, при использовании накопителя оперативной памяти интеграционные тесты MySQL и PostgreSQL выполняются достаточно быстро. Счастливого тестирования!

Код доступен на GitHub .