PostgreSQL: тестирование¶
Что тестовые инструменты hare делают на PostgreSQL сверх того, что Настройка тестов описывает для любой базы.
Повторное использование тестовых баз: reuse_databases¶
Каждый DROP DATABASE заставляет PostgreSQL запросить контрольную точку, и на сервере с включённым
fsync при параллельном запуске тестов это стоит секунд — дольше, чем создать и заполнить базу. При
reuse_databases=True адрес PostgreSQL, в имени базы которого есть место {}, берёт базу из пула
процесса (ReusableTestDatabases), а не создаёт новую со случайным именем:
- место
{}заполняется идентификатором ячейки пула — 32 шестнадцатеричных символа, полученных из номера ячейки (и рабочего процессаpytest-xdist), поэтомуtest_{}даёт одни и те же имена при каждом запуске; - при входе база ячейки создаётся, если её ещё нет, сбрасывается, если осталась от прошлого запуска, и используется как есть, если этот процесс её уже сбросил;
- при выходе вместо
DROP DATABASEбаза сбрасывается, а ячейка возвращается в пул — и тогда, когда блок бросил исключение или не удался сам сброс (тогда база сбрасывается перед следующим использованием); - контекст, в который входят, когда все ячейки заняты (вложенные контексты, несколько подключений в одной конфигурации), получает новую ячейку, поэтому пул вырастает до наибольшего числа баз, используемых одновременно.
Сброс возвращает базу в состояние только что созданной, не удаляя её: завершает другие подключённые к ней
сеансы (pg_terminate_backend), откатывает её подготовленные транзакции, удаляет все схемы, кроме
системных (DROP SCHEMA ... CASCADE — это удаляет и установленные в них расширения: vector, postgis,
citext, btree_gist и другие), заново создаёт public с владельцем, правами и комментарием, как у
новой базы, и очищает настройки уровня базы (ALTER DATABASE ... RESET ALL и
ALTER ROLE ... IN DATABASE ... RESET ALL для каждой роли, у которой они есть). Ни одна из этих команд
не запрашивает контрольную точку. Объекты вне схем — большие объекты, триггеры событий, публикации — не
удаляются.
За пулером в режиме транзакций сброс выполняется на самом
сервере (direct_host) и завершает на базе и серверные сеансы пулера. PgBouncer узнаёт о завершённом
сеансе, только когда выдаёт его, поэтому первый оператор теста упал бы на нём: задайте в PgBouncer тестов
server_check_delay = 0 — тогда он проверяет каждое серверное подключение перед выдачей и заменяет то,
которое завершил сброс.
reuse_databases=None (по умолчанию) читает переменную окружения HARE_TEST_REUSE_DATABASES: 1/true
включает пул, 0/false или отсутствие переменной — нет; любое другое значение даёт
ConfigurationError. Явный аргумент True/False важнее переменной. Адреса SQLite и адреса PostgreSQL
без {} в пул никогда не попадают.
Базы из пула не удаляются при завершении процесса — их забирает следующий запуск, потому что test_{}
снова даёт те же имена. Тест, проверяющий сам жизненный цикл базы, — отказ CREATE DATABASE для
существующего имени, DROP DATABASE при активном соединении — должен передавать reuse_databases=False.